Back to index

glibc  2.9
gai_misc.c
Go to the documentation of this file.
00001 /* Copyright (C) 2001, 2006 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <assert.h>
00021 #include <errno.h>
00022 #include <pthread.h>
00023 #include <stdlib.h>
00024 #include <sys/time.h>
00025 
00026 #include <gai_misc.h>
00027 
00028 
00029 
00030 #ifndef gai_create_helper_thread
00031 # define gai_create_helper_thread __gai_create_helper_thread
00032 
00033 extern inline int
00034 __gai_create_helper_thread (pthread_t *threadp, void *(*tf) (void *),
00035                          void *arg)
00036 {
00037   pthread_attr_t attr;
00038 
00039   /* Make sure the thread is created detached.  */
00040   pthread_attr_init (&attr);
00041   pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
00042 
00043   int ret = pthread_create (threadp, &attr, tf, arg);
00044 
00045   (void) pthread_attr_destroy (&attr);
00046   return ret;
00047 }
00048 #endif
00049 
00050 
00051 /* Pool of request list entries.  */
00052 static struct requestlist **pool;
00053 
00054 /* Number of total and allocated pool entries.  */
00055 static size_t pool_max_size;
00056 static size_t pool_size;
00057 
00058 /* We implement a two dimensional array but allocate each row separately.
00059    The macro below determines how many entries should be used per row.
00060    It should better be a power of two.  */
00061 #define ENTRIES_PER_ROW     32
00062 
00063 /* How many rows we allocate at once.  */
00064 #define ROWS_STEP    8
00065 
00066 /* List of available entries.  */
00067 static struct requestlist *freelist;
00068 
00069 /* Structure list of all currently processed requests.  */
00070 static struct requestlist *requests;
00071 static struct requestlist *requests_tail;
00072 
00073 /* Number of threads currently running.  */
00074 static int nthreads;
00075 
00076 /* Number of threads waiting for work to arrive. */
00077 static int idle_thread_count;
00078 
00079 
00080 /* These are the values used for optimization.  We will probably
00081    create a funcion to set these values.  */
00082 static struct gaiinit optim =
00083 {
00084   20,  /* int gai_threads;  Maximal number of threads.  */
00085   64,  /* int gai_num;             Number of expected simultanious requests. */
00086   0,
00087   0,
00088   0,
00089   0,
00090   1,
00091   0
00092 };
00093 
00094 
00095 /* Since the list is global we need a mutex protecting it.  */
00096 pthread_mutex_t __gai_requests_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
00097 
00098 /* When you add a request to the list and there are idle threads present,
00099    you signal this condition variable. When a thread finishes work, it waits
00100    on this condition variable for a time before it actually exits. */
00101 pthread_cond_t __gai_new_request_notification = PTHREAD_COND_INITIALIZER;
00102 
00103 
00104 /* Functions to handle request list pool.  */
00105 static struct requestlist *
00106 get_elem (void)
00107 {
00108   struct requestlist *result;
00109 
00110   if (freelist == NULL)
00111     {
00112       struct requestlist *new_row;
00113       int cnt;
00114 
00115       if (pool_size + 1 >= pool_max_size)
00116        {
00117          size_t new_max_size = pool_max_size + ROWS_STEP;
00118          struct requestlist **new_tab;
00119 
00120          new_tab = (struct requestlist **)
00121            realloc (pool, new_max_size * sizeof (struct requestlist *));
00122 
00123          if (new_tab == NULL)
00124            return NULL;
00125 
00126          pool_max_size = new_max_size;
00127          pool = new_tab;
00128        }
00129 
00130       /* Allocate the new row.  */
00131       cnt = pool_size == 0 ? optim.gai_num : ENTRIES_PER_ROW;
00132       new_row = (struct requestlist *) calloc (cnt,
00133                                           sizeof (struct requestlist));
00134       if (new_row == NULL)
00135        return NULL;
00136 
00137       pool[pool_size++] = new_row;
00138 
00139       /* Put all the new entries in the freelist.  */
00140       do
00141        {
00142          new_row->next = freelist;
00143          freelist = new_row++;
00144        }
00145       while (--cnt > 0);
00146     }
00147 
00148   result = freelist;
00149   freelist = freelist->next;
00150 
00151   return result;
00152 }
00153 
00154 
00155 struct requestlist *
00156 internal_function
00157 __gai_find_request (const struct gaicb *gaicbp)
00158 {
00159   struct requestlist *runp;
00160 
00161   runp = requests;
00162   while (runp != NULL)
00163     if (runp->gaicbp == gaicbp)
00164       return runp;
00165     else
00166       runp = runp->next;
00167 
00168   return NULL;
00169 }
00170 
00171 
00172 int
00173 internal_function
00174 __gai_remove_request (struct gaicb *gaicbp)
00175 {
00176   struct requestlist *runp;
00177   struct requestlist *lastp;
00178 
00179   runp = requests;
00180   lastp = NULL;
00181   while (runp != NULL)
00182     if (runp->gaicbp == gaicbp)
00183       break;
00184     else
00185       {
00186        lastp = runp;
00187        runp = runp->next;
00188       }
00189 
00190   if (runp == NULL)
00191     /* Not known.  */
00192     return -1;
00193   if (runp->running != 0)
00194     /* Currently handled.  */
00195     return 1;
00196 
00197   /* Dequeue the request.  */
00198   if (lastp == NULL)
00199     requests = runp->next;
00200   else
00201     lastp->next = runp->next;
00202   if (runp == requests_tail)
00203     requests_tail = lastp;
00204 
00205   return 0;
00206 }
00207 
00208 
00209 /* The thread handler.  */
00210 static void *handle_requests (void *arg);
00211 
00212 
00213 /* The main function of the async I/O handling.  It enqueues requests
00214    and if necessary starts and handles threads.  */
00215 struct requestlist *
00216 internal_function
00217 __gai_enqueue_request (struct gaicb *gaicbp)
00218 {
00219   struct requestlist *newp;
00220   struct requestlist *lastp;
00221 
00222   /* Get the mutex.  */
00223   pthread_mutex_lock (&__gai_requests_mutex);
00224 
00225   /* Get a new element for the waiting list.  */
00226   newp = get_elem ();
00227   if (newp == NULL)
00228     {
00229       pthread_mutex_unlock (&__gai_requests_mutex);
00230       __set_errno (EAGAIN);
00231       return NULL;
00232     }
00233   newp->running = 0;
00234   newp->gaicbp = gaicbp;
00235   newp->waiting = NULL;
00236   newp->next = NULL;
00237 
00238   lastp = requests_tail;
00239   if (requests_tail == NULL)
00240     requests = requests_tail = newp;
00241   else
00242     {
00243       requests_tail->next = newp;
00244       requests_tail = newp;
00245     }
00246 
00247   gaicbp->__return = EAI_INPROGRESS;
00248 
00249   /* See if we need to and are able to create a thread.  */
00250   if (nthreads < optim.gai_threads && idle_thread_count == 0)
00251     {
00252       pthread_t thid;
00253 
00254       newp->running = 1;
00255 
00256       /* Now try to start a thread.  */
00257       if (gai_create_helper_thread (&thid, handle_requests, newp) == 0)
00258        /* We managed to enqueue the request.  All errors which can
00259           happen now can be recognized by calls to `gai_error'.  */
00260        ++nthreads;
00261       else
00262        {
00263          if (nthreads == 0)
00264            {
00265              /* We cannot create a thread in the moment and there is
00266                also no thread running.  This is a problem.  `errno' is
00267                set to EAGAIN if this is only a temporary problem.  */
00268              assert (lastp->next == newp);
00269              lastp->next = NULL;
00270              requests_tail = lastp;
00271 
00272              newp->next = freelist;
00273              freelist = newp;
00274 
00275              newp = NULL;
00276            }
00277          else
00278            /* We are not handling the request after all.  */
00279            newp->running = 0;
00280        }
00281     }
00282 
00283   /* Enqueue the request in the request queue.  */
00284   if (newp != NULL)
00285     {
00286       /* If there is a thread waiting for work, then let it know that we
00287         have just given it something to do. */
00288       if (idle_thread_count > 0)
00289        pthread_cond_signal (&__gai_new_request_notification);
00290     }
00291 
00292   /* Release the mutex.  */
00293   pthread_mutex_unlock (&__gai_requests_mutex);
00294 
00295   return newp;
00296 }
00297 
00298 
00299 static void *
00300 __attribute__ ((noreturn))
00301 handle_requests (void *arg)
00302 {
00303   struct requestlist *runp = (struct requestlist *) arg;
00304 
00305   do
00306     {
00307       /* If runp is NULL, then we were created to service the work queue
00308         in general, not to handle any particular request. In that case we
00309         skip the "do work" stuff on the first pass, and go directly to the
00310         "get work off the work queue" part of this loop, which is near the
00311         end. */
00312       if (runp == NULL)
00313        pthread_mutex_lock (&__gai_requests_mutex);
00314       else
00315        {
00316          /* Make the request.  */
00317          struct gaicb *req = runp->gaicbp;
00318          struct requestlist *srchp;
00319          struct requestlist *lastp;
00320 
00321          req->__return = getaddrinfo (req->ar_name, req->ar_service,
00322                                    req->ar_request, &req->ar_result);
00323 
00324          /* Get the mutex.  */
00325          pthread_mutex_lock (&__gai_requests_mutex);
00326 
00327          /* Send the signal to notify about finished processing of the
00328             request.  */
00329          __gai_notify (runp);
00330 
00331          /* Now dequeue the current request.  */
00332          lastp = NULL;
00333          srchp = requests;
00334          while (srchp != runp)
00335            {
00336              lastp = srchp;
00337              srchp = srchp->next;
00338            }
00339          assert (runp->running == 1);
00340 
00341          if (requests_tail == runp)
00342            requests_tail = lastp;
00343          if (lastp == NULL)
00344            requests = requests->next;
00345          else
00346            lastp->next = runp->next;
00347 
00348          /* Free the old element.  */
00349          runp->next = freelist;
00350          freelist = runp;
00351        }
00352 
00353       runp = requests;
00354       while (runp != NULL && runp->running != 0)
00355        runp = runp->next;
00356 
00357       /* If the runlist is empty, then we sleep for a while, waiting for
00358         something to arrive in it. */
00359       if (runp == NULL && optim.gai_idle_time >= 0)
00360        {
00361          struct timeval now;
00362          struct timespec wakeup_time;
00363 
00364          ++idle_thread_count;
00365          gettimeofday (&now, NULL);
00366          wakeup_time.tv_sec = now.tv_sec + optim.gai_idle_time;
00367          wakeup_time.tv_nsec = now.tv_usec * 1000;
00368          if (wakeup_time.tv_nsec > 1000000000)
00369            {
00370              wakeup_time.tv_nsec -= 1000000000;
00371              ++wakeup_time.tv_sec;
00372            }
00373          pthread_cond_timedwait (&__gai_new_request_notification,
00374                               &__gai_requests_mutex, &wakeup_time);
00375          --idle_thread_count;
00376          runp = requests;
00377          while (runp != NULL && runp->running != 0)
00378            runp = runp->next;
00379        }
00380 
00381       if (runp == NULL)
00382        --nthreads;
00383       else
00384        {
00385          /* Mark the request as being worked on.  */
00386          assert (runp->running == 0);
00387          runp->running = 1;
00388 
00389          /* If we have a request to process, and there's still another in
00390             the run list, then we need to either wake up or create a new
00391             thread to service the request that is still in the run list. */
00392          if (requests != NULL)
00393            {
00394              /* There are at least two items in the work queue to work on.
00395                If there are other idle threads, then we should wake them
00396                up for these other work elements; otherwise, we should try
00397                to create a new thread. */
00398              if (idle_thread_count > 0)
00399               pthread_cond_signal (&__gai_new_request_notification);
00400              else if (nthreads < optim.gai_threads)
00401               {
00402                 pthread_t thid;
00403                 pthread_attr_t attr;
00404 
00405                 /* Make sure the thread is created detached.  */
00406                 pthread_attr_init (&attr);
00407                 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
00408 
00409                 /* Now try to start a thread. If we fail, no big deal,
00410                    because we know that there is at least one thread (us)
00411                    that is working on lookup operations. */
00412                 if (pthread_create (&thid, &attr, handle_requests, NULL)
00413                     == 0)
00414                   ++nthreads;
00415               }
00416            }
00417        }
00418 
00419       /* Release the mutex.  */
00420       pthread_mutex_unlock (&__gai_requests_mutex);
00421     }
00422   while (runp != NULL);
00423 
00424   pthread_exit (NULL);
00425 }
00426 
00427 
00428 /* Free allocated resources.  */
00429 libc_freeres_fn (free_res)
00430 {
00431   size_t row;
00432 
00433   for (row = 0; row < pool_max_size; ++row)
00434     free (pool[row]);
00435 
00436   free (pool);
00437 }