(root)/
glibc-2.38/
rt/
aio_suspend.c
       1  /* Suspend until termination of a requests.
       2     Copyright (C) 1997-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  
      20  /* We use an UGLY hack to prevent gcc from finding us cheating.  The
      21     implementations of aio_suspend and aio_suspend64 are identical and so
      22     we want to avoid code duplication by using aliases.  But gcc sees
      23     the different parameter lists and prints a warning.  We define here
      24     a function so that aio_suspend64 has no prototype.  */
      25  #define aio_suspend64 XXX
      26  #include <aio.h>
      27  /* And undo the hack.  */
      28  #undef aio_suspend64
      29  
      30  #include <assert.h>
      31  #include <errno.h>
      32  #include <stdbool.h>
      33  #include <stdlib.h>
      34  #include <sys/time.h>
      35  
      36  #include <libc-lock.h>
      37  #include <aio_misc.h>
      38  #include <pthreadP.h>
      39  #include <shlib-compat.h>
      40  
      41  
      42  struct clparam
      43  {
      44    const struct aiocb *const *list;
      45    struct waitlist *waitlist;
      46    struct requestlist **requestlist;
      47  #ifndef DONT_NEED_AIO_MISC_COND
      48    pthread_cond_t *cond;
      49  #endif
      50    int nent;
      51  };
      52  
      53  
      54  static void
      55  cleanup (void *arg)
      56  {
      57  #ifdef DONT_NEED_AIO_MISC_COND
      58    /* Acquire the mutex.  If pthread_cond_*wait is used this would
      59       happen implicitly.  */
      60    __pthread_mutex_lock (&__aio_requests_mutex);
      61  #endif
      62  
      63    const struct clparam *param = (const struct clparam *) arg;
      64  
      65    /* Now remove the entry in the waiting list for all requests
      66       which didn't terminate.  */
      67    int cnt = param->nent;
      68    while (cnt-- > 0)
      69      if (param->list[cnt] != NULL
      70  	&& param->list[cnt]->__error_code == EINPROGRESS)
      71        {
      72  	struct waitlist **listp;
      73  
      74  	assert (param->requestlist[cnt] != NULL);
      75  
      76  	/* There is the chance that we cannot find our entry anymore. This
      77  	   could happen if the request terminated and restarted again.  */
      78  	listp = &param->requestlist[cnt]->waiting;
      79  	while (*listp != NULL && *listp != &param->waitlist[cnt])
      80  	  listp = &(*listp)->next;
      81  
      82  	if (*listp != NULL)
      83  	  *listp = (*listp)->next;
      84        }
      85  
      86  #ifndef DONT_NEED_AIO_MISC_COND
      87    /* Release the conditional variable.  */
      88    (void) pthread_cond_destroy (param->cond);
      89  #endif
      90  
      91    /* Release the mutex.  */
      92    __pthread_mutex_unlock (&__aio_requests_mutex);
      93  }
      94  
      95  #ifdef DONT_NEED_AIO_MISC_COND
      96  static int
      97  __attribute__ ((noinline))
      98  do_aio_misc_wait (unsigned int *cntr, const struct __timespec64 *timeout)
      99  {
     100    int result = 0;
     101  
     102    AIO_MISC_WAIT (result, *cntr, timeout, 1);
     103  
     104    return result;
     105  }
     106  #endif
     107  
     108  int
     109  ___aio_suspend_time64 (const struct aiocb *const list[], int nent,
     110  		      const struct __timespec64 *timeout)
     111  {
     112    if (__glibc_unlikely (nent < 0))
     113      {
     114        __set_errno (EINVAL);
     115        return -1;
     116      }
     117  
     118    struct waitlist waitlist[nent];
     119    struct requestlist *requestlist[nent];
     120  #ifndef DONT_NEED_AIO_MISC_COND
     121    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
     122  #endif
     123    int cnt;
     124    bool any = false;
     125    int result = 0;
     126    unsigned int cntr = 1;
     127  
     128    /* Request the mutex.  */
     129    __pthread_mutex_lock (&__aio_requests_mutex);
     130  
     131    /* There is not yet a finished request.  Signal the request that
     132       we are working for it.  */
     133    for (cnt = 0; cnt < nent; ++cnt)
     134      if (list[cnt] != NULL)
     135        {
     136  	if (list[cnt]->__error_code == EINPROGRESS)
     137  	  {
     138  	    requestlist[cnt] = __aio_find_req ((aiocb_union *) list[cnt]);
     139  
     140  	    if (requestlist[cnt] != NULL)
     141  	      {
     142  #ifndef DONT_NEED_AIO_MISC_COND
     143  		waitlist[cnt].cond = &cond;
     144  #endif
     145  		waitlist[cnt].result = NULL;
     146  		waitlist[cnt].next = requestlist[cnt]->waiting;
     147  		waitlist[cnt].counterp = &cntr;
     148  		waitlist[cnt].sigevp = NULL;
     149  		requestlist[cnt]->waiting = &waitlist[cnt];
     150  		any = true;
     151  	      }
     152  	    else
     153  	      /* We will never suspend.  */
     154  	      break;
     155  	  }
     156  	else
     157  	  /* We will never suspend.  */
     158  	  break;
     159        }
     160  
     161    struct __timespec64 ts;
     162    if (timeout != NULL)
     163      {
     164        __clock_gettime64 (CLOCK_MONOTONIC, &ts);
     165        ts.tv_sec += timeout->tv_sec;
     166        ts.tv_nsec += timeout->tv_nsec;
     167        if (ts.tv_nsec >= 1000000000)
     168  	{
     169  	  ts.tv_nsec -= 1000000000;
     170  	  ts.tv_sec++;
     171  	}
     172      }
     173  
     174    /* Only if none of the entries is NULL or finished to be wait.  */
     175    if (cnt == nent && any)
     176      {
     177        struct clparam clparam =
     178  	{
     179  	  .list = list,
     180  	  .waitlist = waitlist,
     181  	  .requestlist = requestlist,
     182  #ifndef DONT_NEED_AIO_MISC_COND
     183  	  .cond = &cond,
     184  #endif
     185  	  .nent = nent
     186  	};
     187  
     188  #if PTHREAD_IN_LIBC
     189        __libc_cleanup_region_start (1, cleanup, &clparam);
     190  #else
     191        __pthread_cleanup_push (cleanup, &clparam);
     192  #endif
     193  
     194  #ifdef DONT_NEED_AIO_MISC_COND
     195        result = do_aio_misc_wait (&cntr, timeout == NULL ? NULL : &ts);
     196  #else
     197        struct timespec ts32 = valid_timespec64_to_timespec (ts);
     198        result = pthread_cond_timedwait (&cond, &__aio_requests_mutex,
     199  				       timeout == NULL ? NULL : &ts32);
     200  #endif
     201  
     202  #if PTHREAD_IN_LIBC
     203        __libc_cleanup_region_end (0);
     204  #else
     205        pthread_cleanup_pop (0);
     206  #endif
     207      }
     208  
     209    /* Now remove the entry in the waiting list for all requests
     210       which didn't terminate.  */
     211    while (cnt-- > 0)
     212      if (list[cnt] != NULL && list[cnt]->__error_code == EINPROGRESS)
     213        {
     214  	struct waitlist **listp;
     215  
     216  	assert (requestlist[cnt] != NULL);
     217  
     218  	/* There is the chance that we cannot find our entry anymore. This
     219  	   could happen if the request terminated and restarted again.  */
     220  	listp = &requestlist[cnt]->waiting;
     221  	while (*listp != NULL && *listp != &waitlist[cnt])
     222  	  listp = &(*listp)->next;
     223  
     224  	if (*listp != NULL)
     225  	  *listp = (*listp)->next;
     226        }
     227  
     228  #ifndef DONT_NEED_AIO_MISC_COND
     229    /* Release the conditional variable.  */
     230    if (__glibc_unlikely (pthread_cond_destroy (&cond) != 0))
     231      /* This must never happen.  */
     232      abort ();
     233  #endif
     234  
     235    if (result != 0)
     236      {
     237  #ifndef DONT_NEED_AIO_MISC_COND
     238        /* An error occurred.  Possibly it's ETIMEDOUT.  We have to translate
     239  	 the timeout error report of `pthread_cond_timedwait' to the
     240  	 form expected from `aio_suspend'.  */
     241        if (result == ETIMEDOUT)
     242  	__set_errno (EAGAIN);
     243        else
     244  #endif
     245  	__set_errno (result);
     246  
     247        result = -1;
     248      }
     249  
     250    /* Release the mutex.  */
     251    __pthread_mutex_unlock (&__aio_requests_mutex);
     252  
     253    return result;
     254  }
     255  
     256  #if __TIMESIZE == 64
     257  strong_alias (___aio_suspend_time64, __aio_suspend)
     258  #else /* __TIMESIZE != 64 */
     259  # if PTHREAD_IN_LIBC
     260  libc_hidden_ver (___aio_suspend_time64, __aio_suspend_time64)
     261  /* The conditional is slightly wrong: PTHREAD_IN_LIBC is a stand-in
     262     for whether time64 support is needed.  */
     263  versioned_symbol (libc, ___aio_suspend_time64, __aio_suspend_time64, GLIBC_2_34);
     264  # else
     265  librt_hidden_ver (___aio_suspend_time64, __aio_suspend_time64)
     266  # endif
     267  
     268  int
     269  __aio_suspend (const struct aiocb *const list[], int nent,
     270                 const struct timespec *timeout)
     271  {
     272    struct __timespec64 ts64;
     273  
     274    if (timeout != NULL)
     275      ts64 = valid_timespec_to_timespec64 (*timeout);
     276  
     277    return __aio_suspend_time64 (list, nent, timeout != NULL ? &ts64 : NULL);
     278  }
     279  #endif /* __TIMESPEC64 != 64 */
     280  
     281  #if PTHREAD_IN_LIBC
     282  versioned_symbol (libc, __aio_suspend, aio_suspend, GLIBC_2_34);
     283  versioned_symbol (libc, __aio_suspend, aio_suspend64, GLIBC_2_34);
     284  # if OTHER_SHLIB_COMPAT (librt, GLIBC_2_1, GLIBC_2_34)
     285  compat_symbol (librt, __aio_suspend, aio_suspend, GLIBC_2_1);
     286  compat_symbol (librt, __aio_suspend, aio_suspend64, GLIBC_2_1);
     287  # endif
     288  #else /* !PTHREAD_IN_LIBC */
     289  weak_alias (__aio_suspend, aio_suspend)
     290  weak_alias (__aio_suspend, aio_suspend64)
     291  #endif /* !PTHREAD_IN_LIBC */