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