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