(root)/
glibc-2.38/
rt/
lio_listio-common.c
       1  /* Enqueue and list of read or write requests.  Common code template.
       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  /* The following macros must be defined before including this file:
      20  
      21     LIO_LISTIO         The public symbol (lio_listio or lio_listio64).
      22     AIOCB              Struct tag used by LIO_LISTIO (aiocb or aiocb64).
      23     LIO_LISTIO_OLD     The internal symbol for the compat implementation.
      24     LIO_LISTIO_NEW     The internal symbol for the current implementation.
      25     LIO_OPCODE_BASE    Opcode shift for 64-bit version with 32-bit word size.
      26  
      27     For __WORDSIZE == 64, LIO_LISTIO must always be lio_listio, and
      28     lio_listio64 is automatically defined as well.  */
      29  
      30  #include <bits/wordsize.h>
      31  #if __WORDSIZE == 64
      32  # define lio_listio64 XXX
      33  # include <aio.h>
      34  /* And undo the hack.  */
      35  # undef lio_listio64
      36  #else
      37  # include <aio.h>
      38  #endif
      39  
      40  #include <assert.h>
      41  #include <errno.h>
      42  #include <stdlib.h>
      43  #include <unistd.h>
      44  #include <pthreadP.h>
      45  
      46  #include <aio_misc.h>
      47  
      48  #include <shlib-compat.h>
      49  
      50  
      51  /* We need this special structure to handle asynchronous I/O.  */
      52  struct async_waitlist
      53    {
      54      unsigned int counter;
      55      struct sigevent sigev;
      56      struct waitlist list[0];
      57    };
      58  
      59  
      60  /* The code in glibc 2.1 to glibc 2.4 issued only one event when all
      61     requests submitted with lio_listio finished.  The existing practice
      62     is to issue events for the individual requests as well.  This is
      63     what the new code does.  */
      64  #if SHLIB_COMPAT (librt, GLIBC_2_1, GLIBC_2_4)
      65  # define LIO_MODE(mode) ((mode) & 127)
      66  # define NO_INDIVIDUAL_EVENT_P(mode) ((mode) & 128)
      67  #else
      68  # define LIO_MODE(mode) mode
      69  # define NO_INDIVIDUAL_EVENT_P(mode) 0
      70  #endif
      71  
      72  
      73  static int
      74  lio_listio_internal (int mode, struct AIOCB *const list[], int nent,
      75  		     struct sigevent *sig)
      76  {
      77    struct sigevent defsigev;
      78    struct requestlist *requests[nent];
      79    int cnt;
      80    volatile unsigned int total = 0;
      81    int result = 0;
      82  
      83    if (sig == NULL)
      84      {
      85        defsigev.sigev_notify = SIGEV_NONE;
      86        sig = &defsigev;
      87      }
      88  
      89    /* Request the mutex.  */
      90    __pthread_mutex_lock (&__aio_requests_mutex);
      91  
      92    /* Now we can enqueue all requests.  Since we already acquired the
      93       mutex the enqueue function need not do this.  */
      94    for (cnt = 0; cnt < nent; ++cnt)
      95      if (list[cnt] != NULL && list[cnt]->aio_lio_opcode != LIO_NOP)
      96        {
      97  	if (NO_INDIVIDUAL_EVENT_P (mode))
      98  	  list[cnt]->aio_sigevent.sigev_notify = SIGEV_NONE;
      99  
     100  	requests[cnt] = __aio_enqueue_request ((aiocb_union *) list[cnt],
     101  					       (list[cnt]->aio_lio_opcode
     102  						| LIO_OPCODE_BASE));
     103  
     104  	if (requests[cnt] != NULL)
     105  	  /* Successfully enqueued.  */
     106  	  ++total;
     107  	else
     108  	  /* Signal that we've seen an error.  `errno' and the error code
     109  	     of the aiocb will tell more.  */
     110  	  result = -1;
     111        }
     112      else
     113        requests[cnt] = NULL;
     114  
     115    if (total == 0)
     116      {
     117        /* We don't have anything to do except signalling if we work
     118  	 asynchronously.  */
     119  
     120        /* Release the mutex.  We do this before raising a signal since the
     121  	 signal handler might do a `siglongjmp' and then the mutex is
     122  	 locked forever.  */
     123        __pthread_mutex_unlock (&__aio_requests_mutex);
     124  
     125        if (LIO_MODE (mode) == LIO_NOWAIT)
     126  	__aio_notify_only (sig);
     127  
     128        return result;
     129      }
     130    else if (LIO_MODE (mode) == LIO_WAIT)
     131      {
     132  #ifndef DONT_NEED_AIO_MISC_COND
     133        pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
     134        int oldstate;
     135  #endif
     136        struct waitlist waitlist[nent];
     137  
     138        total = 0;
     139        for (cnt = 0; cnt < nent; ++cnt)
     140  	{
     141  	  assert (requests[cnt] == NULL || list[cnt] != NULL);
     142  
     143  	  if (requests[cnt] != NULL && list[cnt]->aio_lio_opcode != LIO_NOP)
     144  	    {
     145  #ifndef DONT_NEED_AIO_MISC_COND
     146  	      waitlist[cnt].cond = &cond;
     147  #endif
     148  	      waitlist[cnt].result = &result;
     149  	      waitlist[cnt].next = requests[cnt]->waiting;
     150  	      waitlist[cnt].counterp = &total;
     151  	      waitlist[cnt].sigevp = NULL;
     152  	      requests[cnt]->waiting = &waitlist[cnt];
     153  	      ++total;
     154  	    }
     155  	}
     156  
     157  #ifdef DONT_NEED_AIO_MISC_COND
     158        AIO_MISC_WAIT (result, total, NULL, 0);
     159  #else
     160        /* Since `pthread_cond_wait'/`pthread_cond_timedwait' are cancellation
     161  	 points we must be careful.  We added entries to the waiting lists
     162  	 which we must remove.  So defer cancellation for now.  */
     163        pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &oldstate);
     164  
     165        while (total > 0)
     166  	pthread_cond_wait (&cond, &__aio_requests_mutex);
     167  
     168        /* Now it's time to restore the cancellation state.  */
     169        pthread_setcancelstate (oldstate, NULL);
     170  
     171        /* Release the conditional variable.  */
     172        if (pthread_cond_destroy (&cond) != 0)
     173  	/* This must never happen.  */
     174  	abort ();
     175  #endif
     176  
     177        /* If any of the I/O requests failed, return -1 and set errno.  */
     178        if (result != 0)
     179  	{
     180  	  __set_errno (result == EINTR ? EINTR : EIO);
     181  	  result = -1;
     182  	}
     183      }
     184    else
     185      {
     186        struct async_waitlist *waitlist;
     187  
     188        waitlist = (struct async_waitlist *)
     189  	malloc (sizeof (struct async_waitlist)
     190  		+ (nent * sizeof (struct waitlist)));
     191  
     192        if (waitlist == NULL)
     193  	{
     194  	  __set_errno (EAGAIN);
     195  	  result = -1;
     196  	}
     197        else
     198  	{
     199  	  total = 0;
     200  
     201  	  for (cnt = 0; cnt < nent; ++cnt)
     202  	    {
     203  	      assert (requests[cnt] == NULL || list[cnt] != NULL);
     204  
     205  	      if (requests[cnt] != NULL
     206  		  && list[cnt]->aio_lio_opcode != LIO_NOP)
     207  		{
     208  #ifndef DONT_NEED_AIO_MISC_COND
     209  		  waitlist->list[cnt].cond = NULL;
     210  #endif
     211  		  waitlist->list[cnt].result = NULL;
     212  		  waitlist->list[cnt].next = requests[cnt]->waiting;
     213  		  waitlist->list[cnt].counterp = &waitlist->counter;
     214  		  waitlist->list[cnt].sigevp = &waitlist->sigev;
     215  		  requests[cnt]->waiting = &waitlist->list[cnt];
     216  		  ++total;
     217  		}
     218  	    }
     219  
     220  	  waitlist->counter = total;
     221  	  waitlist->sigev = *sig;
     222  	}
     223      }
     224  
     225    /* Release the mutex.  */
     226    __pthread_mutex_unlock (&__aio_requests_mutex);
     227  
     228    return result;
     229  }
     230  
     231  
     232  #if OTHER_SHLIB_COMPAT (librt, GLIBC_2_1, GLIBC_2_4)
     233  int
     234  attribute_compat_text_section
     235  LIO_LISTIO_OLD (int mode, struct AIOCB *const list[], int nent,
     236                  struct sigevent *sig)
     237  {
     238    /* Check arguments.  */
     239    if (mode != LIO_WAIT && mode != LIO_NOWAIT)
     240      {
     241        __set_errno (EINVAL);
     242        return -1;
     243      }
     244  
     245    return lio_listio_internal (mode | LIO_NO_INDIVIDUAL_EVENT, list, nent, sig);
     246  }
     247  compat_symbol (librt, LIO_LISTIO_OLD, LIO_LISTIO, GLIBC_2_1);
     248  # if __WORDSIZE == 64
     249  compat_symbol (librt, LIO_LISTIO_OLD, lio_listio64, GLIBC_2_1);
     250  # endif
     251  #endif /* OTHER_SHLIB_COMPAT */
     252  
     253  
     254  int
     255  LIO_LISTIO_NEW (int mode, struct AIOCB *const list[], int nent,
     256                  struct sigevent *sig)
     257  {
     258      /* Check arguments.  */
     259    if (mode != LIO_WAIT && mode != LIO_NOWAIT)
     260      {
     261        __set_errno (EINVAL);
     262        return -1;
     263      }
     264  
     265    return lio_listio_internal (mode, list, nent, sig);
     266  }
     267  
     268  #if PTHREAD_IN_LIBC
     269  versioned_symbol (libc, LIO_LISTIO_NEW, LIO_LISTIO, GLIBC_2_34);
     270  # if __WORDSIZE == 64
     271  versioned_symbol (libc, LIO_LISTIO_NEW, lio_listio64, GLIBC_2_34);
     272  # endif
     273  # if OTHER_SHLIB_COMPAT (librt, GLIBC_2_4, GLIBC_2_34)
     274  compat_symbol (librt, LIO_LISTIO_NEW, LIO_LISTIO, GLIBC_2_4);
     275  #  if __WORDSIZE == 64
     276  compat_symbol (librt, LIO_LISTIO_NEW, lio_listio64, GLIBC_2_4);
     277  #  endif
     278  # endif /* OTHER_SHLIB_COMPAT */
     279  #else /* !PTHREAD_IN_LIBC */
     280  versioned_symbol (librt, LIO_LISTIO_NEW, LIO_LISTIO, GLIBC_2_4);
     281  # if __WORDSIZE == 64
     282  versioned_symbol (librt, LIO_LISTIO_NEW, lio_listio64, GLIBC_2_4);
     283  # endif
     284  #endif /* !PTHREAD_IN_LIBC */