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