(root)/
glibc-2.38/
resolv/
gai_misc.c
       1  /* Copyright (C) 2001-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <assert.h>
      19  #include <errno.h>
      20  #include <pthread.h>
      21  #include <stdlib.h>
      22  #include <sys/time.h>
      23  
      24  #include <gai_misc.h>
      25  
      26  #if !PTHREAD_IN_LIBC
      27  /* The available function names differ outside of libc.  (In libc, we
      28     need to use hidden aliases to avoid the PLT.)  */
      29  #define __pthread_attr_init pthread_attr_init
      30  #define __pthread_attr_setdetachstate pthread_attr_setdetachstate
      31  #define __pthread_cond_signal pthread_cond_signal
      32  #define __pthread_cond_timedwait pthread_cond_timedwait
      33  #define __pthread_create pthread_create
      34  #define __pthread_exit pthread_exit
      35  #endif
      36  
      37  #ifndef gai_create_helper_thread
      38  # define gai_create_helper_thread __gai_create_helper_thread
      39  
      40  extern inline int
      41  __gai_create_helper_thread (pthread_t *threadp, void *(*tf) (void *),
      42  			    void *arg)
      43  {
      44    pthread_attr_t attr;
      45  
      46    /* Make sure the thread is created detached.  */
      47    __pthread_attr_init (&attr);
      48    __pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
      49  
      50    int ret = __pthread_create (threadp, &attr, tf, arg);
      51  
      52    (void) __pthread_attr_destroy (&attr);
      53    return ret;
      54  }
      55  #endif
      56  
      57  
      58  /* Pool of request list entries.  */
      59  static struct requestlist **pool;
      60  
      61  /* Number of total and allocated pool entries.  */
      62  static size_t pool_max_size;
      63  static size_t pool_size;
      64  
      65  /* We implement a two dimensional array but allocate each row separately.
      66     The macro below determines how many entries should be used per row.
      67     It should better be a power of two.  */
      68  #define ENTRIES_PER_ROW	32
      69  
      70  /* How many rows we allocate at once.  */
      71  #define ROWS_STEP	8
      72  
      73  /* List of available entries.  */
      74  static struct requestlist *freelist;
      75  
      76  /* Structure list of all currently processed requests.  */
      77  static struct requestlist *requests;
      78  static struct requestlist *requests_tail;
      79  
      80  /* Number of threads currently running.  */
      81  static int nthreads;
      82  
      83  /* Number of threads waiting for work to arrive. */
      84  static int idle_thread_count;
      85  
      86  
      87  /* These are the values used for optimization.  We will probably
      88     create a function to set these values.  */
      89  static struct gaiinit optim =
      90  {
      91    20,	/* int gai_threads;	Maximal number of threads.  */
      92    64,	/* int gai_num;		Number of expected simultaneous requests. */
      93    0,
      94    0,
      95    0,
      96    0,
      97    1,
      98    0
      99  };
     100  
     101  
     102  /* Since the list is global we need a mutex protecting it.  */
     103  pthread_mutex_t __gai_requests_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
     104  
     105  /* When you add a request to the list and there are idle threads present,
     106     you signal this condition variable. When a thread finishes work, it waits
     107     on this condition variable for a time before it actually exits. */
     108  pthread_cond_t __gai_new_request_notification = PTHREAD_COND_INITIALIZER;
     109  
     110  
     111  /* Functions to handle request list pool.  */
     112  static struct requestlist *
     113  get_elem (void)
     114  {
     115    struct requestlist *result;
     116  
     117    if (freelist == NULL)
     118      {
     119        struct requestlist *new_row;
     120        int cnt;
     121  
     122        if (pool_size + 1 >= pool_max_size)
     123  	{
     124  	  size_t new_max_size = pool_max_size + ROWS_STEP;
     125  	  struct requestlist **new_tab;
     126  
     127  	  new_tab = (struct requestlist **)
     128  	    realloc (pool, new_max_size * sizeof (struct requestlist *));
     129  
     130  	  if (new_tab == NULL)
     131  	    return NULL;
     132  
     133  	  pool_max_size = new_max_size;
     134  	  pool = new_tab;
     135  	}
     136  
     137        /* Allocate the new row.  */
     138        cnt = pool_size == 0 ? optim.gai_num : ENTRIES_PER_ROW;
     139        new_row = (struct requestlist *) calloc (cnt,
     140  					       sizeof (struct requestlist));
     141        if (new_row == NULL)
     142  	return NULL;
     143  
     144        pool[pool_size++] = new_row;
     145  
     146        /* Put all the new entries in the freelist.  */
     147        do
     148  	{
     149  	  new_row->next = freelist;
     150  	  freelist = new_row++;
     151  	}
     152        while (--cnt > 0);
     153      }
     154  
     155    result = freelist;
     156    freelist = freelist->next;
     157  
     158    return result;
     159  }
     160  
     161  
     162  struct requestlist *
     163  __gai_find_request (const struct gaicb *gaicbp)
     164  {
     165    struct requestlist *runp;
     166  
     167    runp = requests;
     168    while (runp != NULL)
     169      if (runp->gaicbp == gaicbp)
     170        return runp;
     171      else
     172        runp = runp->next;
     173  
     174    return NULL;
     175  }
     176  
     177  
     178  int
     179  __gai_remove_request (struct gaicb *gaicbp)
     180  {
     181    struct requestlist *runp;
     182    struct requestlist *lastp;
     183  
     184    runp = requests;
     185    lastp = NULL;
     186    while (runp != NULL)
     187      if (runp->gaicbp == gaicbp)
     188        break;
     189      else
     190        {
     191  	lastp = runp;
     192  	runp = runp->next;
     193        }
     194  
     195    if (runp == NULL)
     196      /* Not known.  */
     197      return -1;
     198    if (runp->running != 0)
     199      /* Currently handled.  */
     200      return 1;
     201  
     202    /* Dequeue the request.  */
     203    if (lastp == NULL)
     204      requests = runp->next;
     205    else
     206      lastp->next = runp->next;
     207    if (runp == requests_tail)
     208      requests_tail = lastp;
     209  
     210    return 0;
     211  }
     212  
     213  
     214  /* The thread handler.  */
     215  static void *handle_requests (void *arg);
     216  
     217  
     218  /* The main function of the async I/O handling.  It enqueues requests
     219     and if necessary starts and handles threads.  */
     220  struct requestlist *
     221  __gai_enqueue_request (struct gaicb *gaicbp)
     222  {
     223    struct requestlist *newp;
     224    struct requestlist *lastp;
     225  
     226    /* Get the mutex.  */
     227    __pthread_mutex_lock (&__gai_requests_mutex);
     228  
     229    /* Get a new element for the waiting list.  */
     230    newp = get_elem ();
     231    if (newp == NULL)
     232      {
     233        __pthread_mutex_unlock (&__gai_requests_mutex);
     234        __set_errno (EAGAIN);
     235        return NULL;
     236      }
     237    newp->running = 0;
     238    newp->gaicbp = gaicbp;
     239    newp->waiting = NULL;
     240    newp->next = NULL;
     241  
     242    lastp = requests_tail;
     243    if (requests_tail == NULL)
     244      requests = requests_tail = newp;
     245    else
     246      {
     247        requests_tail->next = newp;
     248        requests_tail = newp;
     249      }
     250  
     251    gaicbp->__return = EAI_INPROGRESS;
     252  
     253    /* See if we need to and are able to create a thread.  */
     254    if (nthreads < optim.gai_threads && idle_thread_count == 0)
     255      {
     256        pthread_t thid;
     257  
     258        newp->running = 1;
     259  
     260        /* Now try to start a thread.  */
     261        if (gai_create_helper_thread (&thid, handle_requests, newp) == 0)
     262  	/* We managed to enqueue the request.  All errors which can
     263  	   happen now can be recognized by calls to `gai_error'.  */
     264  	++nthreads;
     265        else
     266  	{
     267  	  if (nthreads == 0)
     268  	    {
     269  	      /* We cannot create a thread in the moment and there is
     270  		 also no thread running.  This is a problem.  `errno' is
     271  		 set to EAGAIN if this is only a temporary problem.  */
     272  	      assert (requests == newp || lastp->next == newp);
     273  	      if (lastp != NULL)
     274  		lastp->next = NULL;
     275  	      else
     276  		requests = NULL;
     277  	      requests_tail = lastp;
     278  
     279  	      newp->next = freelist;
     280  	      freelist = newp;
     281  
     282  	      newp = NULL;
     283  	    }
     284  	  else
     285  	    /* We are not handling the request after all.  */
     286  	    newp->running = 0;
     287  	}
     288      }
     289  
     290    /* Enqueue the request in the request queue.  */
     291    if (newp != NULL)
     292      {
     293        /* If there is a thread waiting for work, then let it know that we
     294  	 have just given it something to do. */
     295        if (idle_thread_count > 0)
     296  	__pthread_cond_signal (&__gai_new_request_notification);
     297      }
     298  
     299    /* Release the mutex.  */
     300    __pthread_mutex_unlock (&__gai_requests_mutex);
     301  
     302    return newp;
     303  }
     304  
     305  
     306  static void *
     307  __attribute__ ((noreturn))
     308  handle_requests (void *arg)
     309  {
     310    struct requestlist *runp = (struct requestlist *) arg;
     311  
     312    do
     313      {
     314        /* If runp is NULL, then we were created to service the work queue
     315  	 in general, not to handle any particular request. In that case we
     316  	 skip the "do work" stuff on the first pass, and go directly to the
     317  	 "get work off the work queue" part of this loop, which is near the
     318  	 end. */
     319        if (runp == NULL)
     320  	__pthread_mutex_lock (&__gai_requests_mutex);
     321        else
     322  	{
     323  	  /* Make the request.  */
     324  	  struct gaicb *req = runp->gaicbp;
     325  	  struct requestlist *srchp;
     326  	  struct requestlist *lastp;
     327  
     328  	  req->__return = getaddrinfo (req->ar_name, req->ar_service,
     329  				       req->ar_request, &req->ar_result);
     330  
     331  	  /* Get the mutex.  */
     332  	  __pthread_mutex_lock (&__gai_requests_mutex);
     333  
     334  	  /* Send the signal to notify about finished processing of the
     335  	     request.  */
     336  	  __gai_notify (runp);
     337  
     338  	  /* Now dequeue the current request.  */
     339  	  lastp = NULL;
     340  	  srchp = requests;
     341  	  while (srchp != runp)
     342  	    {
     343  	      lastp = srchp;
     344  	      srchp = srchp->next;
     345  	    }
     346  	  assert (runp->running == 1);
     347  
     348  	  if (requests_tail == runp)
     349  	    requests_tail = lastp;
     350  	  if (lastp == NULL)
     351  	    requests = requests->next;
     352  	  else
     353  	    lastp->next = runp->next;
     354  
     355  	  /* Free the old element.  */
     356  	  runp->next = freelist;
     357  	  freelist = runp;
     358  	}
     359  
     360        runp = requests;
     361        while (runp != NULL && runp->running != 0)
     362  	runp = runp->next;
     363  
     364        /* If the runlist is empty, then we sleep for a while, waiting for
     365  	 something to arrive in it. */
     366        if (runp == NULL && optim.gai_idle_time >= 0)
     367  	{
     368  	  struct timespec now;
     369  	  struct timespec wakeup_time;
     370  
     371  	  ++idle_thread_count;
     372            __clock_gettime (CLOCK_REALTIME, &now);
     373  	  wakeup_time.tv_sec = now.tv_sec + optim.gai_idle_time;
     374  	  wakeup_time.tv_nsec = now.tv_nsec;
     375  	  if (wakeup_time.tv_nsec >= 1000000000)
     376  	    {
     377  	      wakeup_time.tv_nsec -= 1000000000;
     378  	      ++wakeup_time.tv_sec;
     379  	    }
     380  	  __pthread_cond_timedwait (&__gai_new_request_notification,
     381  				    &__gai_requests_mutex, &wakeup_time);
     382  	  --idle_thread_count;
     383  	  runp = requests;
     384  	  while (runp != NULL && runp->running != 0)
     385  	    runp = runp->next;
     386  	}
     387  
     388        if (runp == NULL)
     389  	--nthreads;
     390        else
     391  	{
     392  	  /* Mark the request as being worked on.  */
     393  	  assert (runp->running == 0);
     394  	  runp->running = 1;
     395  
     396  	  /* If we have a request to process, and there's still another in
     397  	     the run list, then we need to either wake up or create a new
     398  	     thread to service the request that is still in the run list. */
     399  	  if (requests != NULL)
     400  	    {
     401  	      /* There are at least two items in the work queue to work on.
     402  		 If there are other idle threads, then we should wake them
     403  		 up for these other work elements; otherwise, we should try
     404  		 to create a new thread. */
     405  	      if (idle_thread_count > 0)
     406  		__pthread_cond_signal (&__gai_new_request_notification);
     407  	      else if (nthreads < optim.gai_threads)
     408  		{
     409  		  pthread_t thid;
     410  		  pthread_attr_t attr;
     411  
     412  		  /* Make sure the thread is created detached.  */
     413  		  __pthread_attr_init (&attr);
     414  		  __pthread_attr_setdetachstate (&attr,
     415  						 PTHREAD_CREATE_DETACHED);
     416  
     417  		  /* Now try to start a thread. If we fail, no big deal,
     418  		     because we know that there is at least one thread (us)
     419  		     that is working on lookup operations. */
     420  		  if (__pthread_create (&thid, &attr, handle_requests, NULL)
     421  		      == 0)
     422  		    ++nthreads;
     423  		}
     424  	    }
     425  	}
     426  
     427        /* Release the mutex.  */
     428        __pthread_mutex_unlock (&__gai_requests_mutex);
     429      }
     430    while (runp != NULL);
     431  
     432    __pthread_exit (NULL);
     433  }
     434  
     435  
     436  /* Free allocated resources.  */
     437  #if !PTHREAD_IN_LIBC
     438  __attribute__ ((__destructor__)) static
     439  #endif
     440  void
     441  __gai_freemem (void)
     442  {
     443    size_t row;
     444  
     445    for (row = 0; row < pool_max_size; ++row)
     446      free (pool[row]);
     447  
     448    free (pool);
     449  }