Blame resolv/gai_misc.c

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