Blame resolv/gai_misc.c

Packit Service 82fcde
/* Copyright (C) 2001-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
   Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <assert.h>
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <pthread.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <sys/time.h>
Packit Service 82fcde
Packit Service 82fcde
#include <gai_misc.h>
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
#ifndef gai_create_helper_thread
Packit Service 82fcde
# define gai_create_helper_thread __gai_create_helper_thread
Packit Service 82fcde
Packit Service 82fcde
extern inline int
Packit Service 82fcde
__gai_create_helper_thread (pthread_t *threadp, void *(*tf) (void *),
Packit Service 82fcde
			    void *arg)
Packit Service 82fcde
{
Packit Service 82fcde
  pthread_attr_t attr;
Packit Service 82fcde
Packit Service 82fcde
  /* Make sure the thread is created detached.  */
Packit Service 82fcde
  pthread_attr_init (&attr);
Packit Service 82fcde
  pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
Packit Service 82fcde
Packit Service 82fcde
  int ret = pthread_create (threadp, &attr, tf, arg);
Packit Service 82fcde
Packit Service 82fcde
  (void) pthread_attr_destroy (&attr);
Packit Service 82fcde
  return ret;
Packit Service 82fcde
}
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Pool of request list entries.  */
Packit Service 82fcde
static struct requestlist **pool;
Packit Service 82fcde
Packit Service 82fcde
/* Number of total and allocated pool entries.  */
Packit Service 82fcde
static size_t pool_max_size;
Packit Service 82fcde
static size_t pool_size;
Packit Service 82fcde
Packit Service 82fcde
/* We implement a two dimensional array but allocate each row separately.
Packit Service 82fcde
   The macro below determines how many entries should be used per row.
Packit Service 82fcde
   It should better be a power of two.  */
Packit Service 82fcde
#define ENTRIES_PER_ROW	32
Packit Service 82fcde
Packit Service 82fcde
/* How many rows we allocate at once.  */
Packit Service 82fcde
#define ROWS_STEP	8
Packit Service 82fcde
Packit Service 82fcde
/* List of available entries.  */
Packit Service 82fcde
static struct requestlist *freelist;
Packit Service 82fcde
Packit Service 82fcde
/* Structure list of all currently processed requests.  */
Packit Service 82fcde
static struct requestlist *requests;
Packit Service 82fcde
static struct requestlist *requests_tail;
Packit Service 82fcde
Packit Service 82fcde
/* Number of threads currently running.  */
Packit Service 82fcde
static int nthreads;
Packit Service 82fcde
Packit Service 82fcde
/* Number of threads waiting for work to arrive. */
Packit Service 82fcde
static int idle_thread_count;
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* These are the values used for optimization.  We will probably
Packit Service 82fcde
   create a funcion to set these values.  */
Packit Service 82fcde
static struct gaiinit optim =
Packit Service 82fcde
{
Packit Service 82fcde
  20,	/* int gai_threads;	Maximal number of threads.  */
Packit Service 82fcde
  64,	/* int gai_num;		Number of expected simultanious requests. */
Packit Service 82fcde
  0,
Packit Service 82fcde
  0,
Packit Service 82fcde
  0,
Packit Service 82fcde
  0,
Packit Service 82fcde
  1,
Packit Service 82fcde
  0
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Since the list is global we need a mutex protecting it.  */
Packit Service 82fcde
pthread_mutex_t __gai_requests_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
Packit Service 82fcde
Packit Service 82fcde
/* When you add a request to the list and there are idle threads present,
Packit Service 82fcde
   you signal this condition variable. When a thread finishes work, it waits
Packit Service 82fcde
   on this condition variable for a time before it actually exits. */
Packit Service 82fcde
pthread_cond_t __gai_new_request_notification = PTHREAD_COND_INITIALIZER;
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Functions to handle request list pool.  */
Packit Service 82fcde
static struct requestlist *
Packit Service 82fcde
get_elem (void)
Packit Service 82fcde
{
Packit Service 82fcde
  struct requestlist *result;
Packit Service 82fcde
Packit Service 82fcde
  if (freelist == NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      struct requestlist *new_row;
Packit Service 82fcde
      int cnt;
Packit Service 82fcde
Packit Service 82fcde
      if (pool_size + 1 >= pool_max_size)
Packit Service 82fcde
	{
Packit Service 82fcde
	  size_t new_max_size = pool_max_size + ROWS_STEP;
Packit Service 82fcde
	  struct requestlist **new_tab;
Packit Service 82fcde
Packit Service 82fcde
	  new_tab = (struct requestlist **)
Packit Service 82fcde
	    realloc (pool, new_max_size * sizeof (struct requestlist *));
Packit Service 82fcde
Packit Service 82fcde
	  if (new_tab == NULL)
Packit Service 82fcde
	    return NULL;
Packit Service 82fcde
Packit Service 82fcde
	  pool_max_size = new_max_size;
Packit Service 82fcde
	  pool = new_tab;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      /* Allocate the new row.  */
Packit Service 82fcde
      cnt = pool_size == 0 ? optim.gai_num : ENTRIES_PER_ROW;
Packit Service 82fcde
      new_row = (struct requestlist *) calloc (cnt,
Packit Service 82fcde
					       sizeof (struct requestlist));
Packit Service 82fcde
      if (new_row == NULL)
Packit Service 82fcde
	return NULL;
Packit Service 82fcde
Packit Service 82fcde
      pool[pool_size++] = new_row;
Packit Service 82fcde
Packit Service 82fcde
      /* Put all the new entries in the freelist.  */
Packit Service 82fcde
      do
Packit Service 82fcde
	{
Packit Service 82fcde
	  new_row->next = freelist;
Packit Service 82fcde
	  freelist = new_row++;
Packit Service 82fcde
	}
Packit Service 82fcde
      while (--cnt > 0);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  result = freelist;
Packit Service 82fcde
  freelist = freelist->next;
Packit Service 82fcde
Packit Service 82fcde
  return result;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
struct requestlist *
Packit Service 82fcde
__gai_find_request (const struct gaicb *gaicbp)
Packit Service 82fcde
{
Packit Service 82fcde
  struct requestlist *runp;
Packit Service 82fcde
Packit Service 82fcde
  runp = requests;
Packit Service 82fcde
  while (runp != NULL)
Packit Service 82fcde
    if (runp->gaicbp == gaicbp)
Packit Service 82fcde
      return runp;
Packit Service 82fcde
    else
Packit Service 82fcde
      runp = runp->next;
Packit Service 82fcde
Packit Service 82fcde
  return NULL;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
int
Packit Service 82fcde
__gai_remove_request (struct gaicb *gaicbp)
Packit Service 82fcde
{
Packit Service 82fcde
  struct requestlist *runp;
Packit Service 82fcde
  struct requestlist *lastp;
Packit Service 82fcde
Packit Service 82fcde
  runp = requests;
Packit Service 82fcde
  lastp = NULL;
Packit Service 82fcde
  while (runp != NULL)
Packit Service 82fcde
    if (runp->gaicbp == gaicbp)
Packit Service 82fcde
      break;
Packit Service 82fcde
    else
Packit Service 82fcde
      {
Packit Service 82fcde
	lastp = runp;
Packit Service 82fcde
	runp = runp->next;
Packit Service 82fcde
      }
Packit Service 82fcde
Packit Service 82fcde
  if (runp == NULL)
Packit Service 82fcde
    /* Not known.  */
Packit Service 82fcde
    return -1;
Packit Service 82fcde
  if (runp->running != 0)
Packit Service 82fcde
    /* Currently handled.  */
Packit Service 82fcde
    return 1;
Packit Service 82fcde
Packit Service 82fcde
  /* Dequeue the request.  */
Packit Service 82fcde
  if (lastp == NULL)
Packit Service 82fcde
    requests = runp->next;
Packit Service 82fcde
  else
Packit Service 82fcde
    lastp->next = runp->next;
Packit Service 82fcde
  if (runp == requests_tail)
Packit Service 82fcde
    requests_tail = lastp;
Packit Service 82fcde
Packit Service 82fcde
  return 0;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* The thread handler.  */
Packit Service 82fcde
static void *handle_requests (void *arg);
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* The main function of the async I/O handling.  It enqueues requests
Packit Service 82fcde
   and if necessary starts and handles threads.  */
Packit Service 82fcde
struct requestlist *
Packit Service 82fcde
__gai_enqueue_request (struct gaicb *gaicbp)
Packit Service 82fcde
{
Packit Service 82fcde
  struct requestlist *newp;
Packit Service 82fcde
  struct requestlist *lastp;
Packit Service 82fcde
Packit Service 82fcde
  /* Get the mutex.  */
Packit Service 82fcde
  pthread_mutex_lock (&__gai_requests_mutex);
Packit Service 82fcde
Packit Service 82fcde
  /* Get a new element for the waiting list.  */
Packit Service 82fcde
  newp = get_elem ();
Packit Service 82fcde
  if (newp == NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      pthread_mutex_unlock (&__gai_requests_mutex);
Packit Service 82fcde
      __set_errno (EAGAIN);
Packit Service 82fcde
      return NULL;
Packit Service 82fcde
    }
Packit Service 82fcde
  newp->running = 0;
Packit Service 82fcde
  newp->gaicbp = gaicbp;
Packit Service 82fcde
  newp->waiting = NULL;
Packit Service 82fcde
  newp->next = NULL;
Packit Service 82fcde
Packit Service 82fcde
  lastp = requests_tail;
Packit Service 82fcde
  if (requests_tail == NULL)
Packit Service 82fcde
    requests = requests_tail = newp;
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      requests_tail->next = newp;
Packit Service 82fcde
      requests_tail = newp;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  gaicbp->__return = EAI_INPROGRESS;
Packit Service 82fcde
Packit Service 82fcde
  /* See if we need to and are able to create a thread.  */
Packit Service 82fcde
  if (nthreads < optim.gai_threads && idle_thread_count == 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      pthread_t thid;
Packit Service 82fcde
Packit Service 82fcde
      newp->running = 1;
Packit Service 82fcde
Packit Service 82fcde
      /* Now try to start a thread.  */
Packit Service 82fcde
      if (gai_create_helper_thread (&thid, handle_requests, newp) == 0)
Packit Service 82fcde
	/* We managed to enqueue the request.  All errors which can
Packit Service 82fcde
	   happen now can be recognized by calls to `gai_error'.  */
Packit Service 82fcde
	++nthreads;
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  if (nthreads == 0)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      /* We cannot create a thread in the moment and there is
Packit Service 82fcde
		 also no thread running.  This is a problem.  `errno' is
Packit Service 82fcde
		 set to EAGAIN if this is only a temporary problem.  */
Packit Service 1c5418
	      assert (lastp->next == newp);
Packit Service 1c5418
	      lastp->next = NULL;
Packit Service 82fcde
	      requests_tail = lastp;
Packit Service 82fcde
Packit Service 82fcde
	      newp->next = freelist;
Packit Service 82fcde
	      freelist = newp;
Packit Service 82fcde
Packit Service 82fcde
	      newp = NULL;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  else
Packit Service 82fcde
	    /* We are not handling the request after all.  */
Packit Service 82fcde
	    newp->running = 0;
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Enqueue the request in the request queue.  */
Packit Service 82fcde
  if (newp != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* If there is a thread waiting for work, then let it know that we
Packit Service 82fcde
	 have just given it something to do. */
Packit Service 82fcde
      if (idle_thread_count > 0)
Packit Service 82fcde
	pthread_cond_signal (&__gai_new_request_notification);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Release the mutex.  */
Packit Service 82fcde
  pthread_mutex_unlock (&__gai_requests_mutex);
Packit Service 82fcde
Packit Service 82fcde
  return newp;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
static void *
Packit Service 82fcde
__attribute__ ((noreturn))
Packit Service 82fcde
handle_requests (void *arg)
Packit Service 82fcde
{
Packit Service 82fcde
  struct requestlist *runp = (struct requestlist *) arg;
Packit Service 82fcde
Packit Service 82fcde
  do
Packit Service 82fcde
    {
Packit Service 82fcde
      /* If runp is NULL, then we were created to service the work queue
Packit Service 82fcde
	 in general, not to handle any particular request. In that case we
Packit Service 82fcde
	 skip the "do work" stuff on the first pass, and go directly to the
Packit Service 82fcde
	 "get work off the work queue" part of this loop, which is near the
Packit Service 82fcde
	 end. */
Packit Service 82fcde
      if (runp == NULL)
Packit Service 82fcde
	pthread_mutex_lock (&__gai_requests_mutex);
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* Make the request.  */
Packit Service 82fcde
	  struct gaicb *req = runp->gaicbp;
Packit Service 82fcde
	  struct requestlist *srchp;
Packit Service 82fcde
	  struct requestlist *lastp;
Packit Service 82fcde
Packit Service 82fcde
	  req->__return = getaddrinfo (req->ar_name, req->ar_service,
Packit Service 82fcde
				       req->ar_request, &req->ar_result);
Packit Service 82fcde
Packit Service 82fcde
	  /* Get the mutex.  */
Packit Service 82fcde
	  pthread_mutex_lock (&__gai_requests_mutex);
Packit Service 82fcde
Packit Service 82fcde
	  /* Send the signal to notify about finished processing of the
Packit Service 82fcde
	     request.  */
Packit Service 82fcde
	  __gai_notify (runp);
Packit Service 82fcde
Packit Service 82fcde
	  /* Now dequeue the current request.  */
Packit Service 82fcde
	  lastp = NULL;
Packit Service 82fcde
	  srchp = requests;
Packit Service 82fcde
	  while (srchp != runp)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      lastp = srchp;
Packit Service 82fcde
	      srchp = srchp->next;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  assert (runp->running == 1);
Packit Service 82fcde
Packit Service 82fcde
	  if (requests_tail == runp)
Packit Service 82fcde
	    requests_tail = lastp;
Packit Service 82fcde
	  if (lastp == NULL)
Packit Service 82fcde
	    requests = requests->next;
Packit Service 82fcde
	  else
Packit Service 82fcde
	    lastp->next = runp->next;
Packit Service 82fcde
Packit Service 82fcde
	  /* Free the old element.  */
Packit Service 82fcde
	  runp->next = freelist;
Packit Service 82fcde
	  freelist = runp;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      runp = requests;
Packit Service 82fcde
      while (runp != NULL && runp->running != 0)
Packit Service 82fcde
	runp = runp->next;
Packit Service 82fcde
Packit Service 82fcde
      /* If the runlist is empty, then we sleep for a while, waiting for
Packit Service 82fcde
	 something to arrive in it. */
Packit Service 82fcde
      if (runp == NULL && optim.gai_idle_time >= 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  struct timeval now;
Packit Service 82fcde
	  struct timespec wakeup_time;
Packit Service 82fcde
Packit Service 82fcde
	  ++idle_thread_count;
Packit Service 82fcde
	  gettimeofday (&now, NULL);
Packit Service 82fcde
	  wakeup_time.tv_sec = now.tv_sec + optim.gai_idle_time;
Packit Service 82fcde
	  wakeup_time.tv_nsec = now.tv_usec * 1000;
Packit Service 82fcde
	  if (wakeup_time.tv_nsec >= 1000000000)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      wakeup_time.tv_nsec -= 1000000000;
Packit Service 82fcde
	      ++wakeup_time.tv_sec;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  pthread_cond_timedwait (&__gai_new_request_notification,
Packit Service 82fcde
				  &__gai_requests_mutex, &wakeup_time);
Packit Service 82fcde
	  --idle_thread_count;
Packit Service 82fcde
	  runp = requests;
Packit Service 82fcde
	  while (runp != NULL && runp->running != 0)
Packit Service 82fcde
	    runp = runp->next;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      if (runp == NULL)
Packit Service 82fcde
	--nthreads;
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* Mark the request as being worked on.  */
Packit Service 82fcde
	  assert (runp->running == 0);
Packit Service 82fcde
	  runp->running = 1;
Packit Service 82fcde
Packit Service 82fcde
	  /* If we have a request to process, and there's still another in
Packit Service 82fcde
	     the run list, then we need to either wake up or create a new
Packit Service 82fcde
	     thread to service the request that is still in the run list. */
Packit Service 82fcde
	  if (requests != NULL)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      /* There are at least two items in the work queue to work on.
Packit Service 82fcde
		 If there are other idle threads, then we should wake them
Packit Service 82fcde
		 up for these other work elements; otherwise, we should try
Packit Service 82fcde
		 to create a new thread. */
Packit Service 82fcde
	      if (idle_thread_count > 0)
Packit Service 82fcde
		pthread_cond_signal (&__gai_new_request_notification);
Packit Service 82fcde
	      else if (nthreads < optim.gai_threads)
Packit Service 82fcde
		{
Packit Service 82fcde
		  pthread_t thid;
Packit Service 82fcde
		  pthread_attr_t attr;
Packit Service 82fcde
Packit Service 82fcde
		  /* Make sure the thread is created detached.  */
Packit Service 82fcde
		  pthread_attr_init (&attr);
Packit Service 82fcde
		  pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
Packit Service 82fcde
Packit Service 82fcde
		  /* Now try to start a thread. If we fail, no big deal,
Packit Service 82fcde
		     because we know that there is at least one thread (us)
Packit Service 82fcde
		     that is working on lookup operations. */
Packit Service 82fcde
		  if (pthread_create (&thid, &attr, handle_requests, NULL)
Packit Service 82fcde
		      == 0)
Packit Service 82fcde
		    ++nthreads;
Packit Service 82fcde
		}
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      /* Release the mutex.  */
Packit Service 82fcde
      pthread_mutex_unlock (&__gai_requests_mutex);
Packit Service 82fcde
    }
Packit Service 82fcde
  while (runp != NULL);
Packit Service 82fcde
Packit Service 82fcde
  pthread_exit (NULL);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Free allocated resources.  */
Packit Service 82fcde
libc_freeres_fn (free_res)
Packit Service 82fcde
{
Packit Service 82fcde
  size_t row;
Packit Service 82fcde
Packit Service 82fcde
  for (row = 0; row < pool_max_size; ++row)
Packit Service 82fcde
    free (pool[row]);
Packit Service 82fcde
Packit Service 82fcde
  free (pool);
Packit Service 82fcde
}