Implementation of request queue handling and actual lookup for
[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 Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    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    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 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 handle_requests (void *arg)
285 {
286   struct requestlist *runp = (struct requestlist *) arg;
287
288   do
289     {
290       /* If runp is NULL, then we were created to service the work queue
291          in general, not to handle any particular request. In that case we
292          skip the "do work" stuff on the first pass, and go directly to the
293          "get work off the work queue" part of this loop, which is near the
294          end. */
295       if (runp == NULL)
296         pthread_mutex_lock (&__gai_requests_mutex);
297       else
298         {
299           /* Make the request.  */
300           struct gaicb *req = runp->gaicbp;
301           struct requestlist *srchp;
302           struct requestlist *lastp;
303
304           req->__return = getaddrinfo (req->ar_name, req->ar_service,
305                                        req->ar_request, &req->ar_result);
306
307           /* Get the mutex.  */
308           pthread_mutex_lock (&__gai_requests_mutex);
309
310           /* Send the signal to notify about finished processing of the
311              request.  */
312           __gai_notify (runp);
313
314           /* Now dequeue the current request.  */
315           lastp = NULL;
316           srchp = requests;
317           while (srchp != runp)
318             {
319               lastp = srchp;
320               srchp = srchp->next;
321             }
322           assert (runp->running == 1);
323
324           if (requests_tail == runp)
325             requests_tail = lastp;
326           if (lastp == NULL)
327             requests = requests->next;
328           else
329             lastp->next = runp->next;
330
331           /* Free the old element.  */
332           runp->next = freelist;
333           freelist = runp;
334         }
335
336       runp = requests;
337       while (runp != NULL && runp->running != 0)
338         runp = runp->next;
339
340       /* If the runlist is empty, then we sleep for a while, waiting for
341          something to arrive in it. */
342       if (runp == NULL && optim.gai_idle_time >= 0)
343         {
344           struct timeval now;
345           struct timespec wakeup_time;
346
347           ++idle_thread_count;
348           gettimeofday (&now, NULL);
349           wakeup_time.tv_sec = now.tv_sec + optim.gai_idle_time;
350           wakeup_time.tv_nsec = now.tv_usec * 1000;
351           if (wakeup_time.tv_nsec > 1000000000)
352             {
353               wakeup_time.tv_nsec -= 1000000000;
354               ++wakeup_time.tv_sec;
355             }
356           pthread_cond_timedwait (&__gai_new_request_notification,
357                                   &__gai_requests_mutex, &wakeup_time);
358           --idle_thread_count;
359           runp = requests;
360           while (runp != NULL && runp->running != 0)
361             runp = runp->next;
362         }
363
364       if (runp == NULL)
365         --nthreads;
366       else
367         {
368           /* Mark the request as being worked on.  */
369           assert (runp->running == 0);
370           runp->running = 1;
371
372           /* If we have a request to process, and there's still another in
373              the run list, then we need to either wake up or create a new
374              thread to service the request that is still in the run list. */
375           if (requests != NULL)
376             {
377               /* There are at least two items in the work queue to work on.
378                  If there are other idle threads, then we should wake them
379                  up for these other work elements; otherwise, we should try
380                  to create a new thread. */
381               if (idle_thread_count > 0)
382                 pthread_cond_signal (&__gai_new_request_notification);
383               else if (nthreads < optim.gai_threads)
384                 {
385                   pthread_t thid;
386                   pthread_attr_t attr;
387
388                   /* Make sure the thread is created detached.  */
389                   pthread_attr_init (&attr);
390                   pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
391
392                   /* Now try to start a thread. If we fail, no big deal,
393                      because we know that there is at least one thread (us)
394                      that is working on lookup operations. */
395                   if (pthread_create (&thid, &attr, handle_requests, NULL)
396                       == 0)
397                     ++nthreads;
398                 }
399             }
400         }
401
402       /* Release the mutex.  */
403       pthread_mutex_unlock (&__gai_requests_mutex);
404     }
405   while (runp != NULL);
406
407   pthread_exit (NULL);
408 }
409
410
411 /* Free allocated resources.  */
412 static void
413 __attribute__ ((unused))
414 free_res (void)
415 {
416   size_t row;
417
418   for (row = 0; row < pool_max_size; ++row)
419     free (pool[row]);
420
421   free (pool);
422 }
423 text_set_element (__libc_subfreeres, free_res);