.
[glibc/history.git] / resolv / gai_misc.c
blobb3334f38efd4ff937808d0e1bf368eb283b61667
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.
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.
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.
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. */
20 #include <assert.h>
21 #include <errno.h>
22 #include <pthread.h>
23 #include <stdlib.h>
24 #include <sys/time.h>
26 #include "gai_misc.h"
30 /* Pool of request list entries. */
31 static struct requestlist **pool;
33 /* Number of total and allocated pool entries. */
34 static size_t pool_max_size;
35 static size_t pool_size;
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
42 /* How many rows we allocate at once. */
43 #define ROWS_STEP 8
45 /* List of available entries. */
46 static struct requestlist *freelist;
48 /* Structure list of all currently processed requests. */
49 static struct requestlist *requests;
50 static struct requestlist *requests_tail;
52 /* Number of threads currently running. */
53 static int nthreads;
55 /* Number of threads waiting for work to arrive. */
56 static int idle_thread_count;
59 /* These are the values used for optimization. We will probably
60 create a funcion to set these values. */
61 static struct gaiinit optim =
63 20, /* int gai_threads; Maximal number of threads. */
64 64, /* int gai_num; Number of expected simultanious requests. */
74 /* Since the list is global we need a mutex protecting it. */
75 pthread_mutex_t __gai_requests_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
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;
83 /* Functions to handle request list pool. */
84 static struct requestlist *
85 get_elem (void)
87 struct requestlist *result;
89 if (freelist == NULL)
91 struct requestlist *new_row;
92 int cnt;
94 if (pool_size + 1 >= pool_max_size)
96 size_t new_max_size = pool_max_size + ROWS_STEP;
97 struct requestlist **new_tab;
99 new_tab = (struct requestlist **)
100 realloc (pool, new_max_size * sizeof (struct requestlist *));
102 if (new_tab == NULL)
103 return NULL;
105 pool_max_size = new_max_size;
106 pool = new_tab;
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;
116 pool[pool_size++] = new_row;
118 /* Put all the new entries in the freelist. */
121 new_row->next = freelist;
122 freelist = new_row++;
124 while (--cnt > 0);
127 result = freelist;
128 freelist = freelist->next;
130 return result;
134 struct requestlist *
135 internal_function
136 __gai_find_request (const struct gaicb *gaicbp)
138 struct requestlist *runp;
140 runp = requests;
141 while (runp != NULL)
142 if (runp->gaicbp == gaicbp)
143 return runp;
144 else
145 runp = runp->next;
147 return NULL;
152 internal_function
153 __gai_remove_request (struct gaicb *gaicbp)
155 struct requestlist *runp;
156 struct requestlist *lastp;
158 runp = requests;
159 lastp = NULL;
160 while (runp != NULL)
161 if (runp->gaicbp == gaicbp)
162 break;
163 else
165 lastp = runp;
166 runp = runp->next;
169 if (runp == NULL)
170 /* Not known. */
171 return -1;
172 if (runp->running != 0)
173 /* Currently handled. */
174 return 1;
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;
184 return 0;
188 /* The thread handler. */
189 static void *handle_requests (void *arg);
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)
198 struct requestlist *newp;
199 struct requestlist *lastp;
201 /* Get the mutex. */
202 pthread_mutex_lock (&__gai_requests_mutex);
204 /* Get a new element for the waiting list. */
205 newp = get_elem ();
206 if (newp == NULL)
208 pthread_mutex_unlock (&__gai_requests_mutex);
209 __set_errno (EAGAIN);
210 return NULL;
212 newp->running = 0;
213 newp->gaicbp = gaicbp;
214 newp->waiting = NULL;
215 newp->next = NULL;
217 lastp = requests_tail;
218 if (requests_tail == NULL)
219 requests = requests_tail = newp;
220 else
222 requests_tail->next = newp;
223 requests_tail = newp;
226 gaicbp->__return = EAI_INPROGRESS;
228 /* See if we need to and are able to create a thread. */
229 if (nthreads < optim.gai_threads && idle_thread_count == 0)
231 pthread_t thid;
232 pthread_attr_t attr;
234 newp->running = 1;
236 /* Make sure the thread is created detached. */
237 pthread_attr_init (&attr);
238 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
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
247 if (nthreads == 0)
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;
256 newp->next = freelist;
257 freelist = newp;
259 newp = NULL;
261 else
262 /* We are not handling the request after all. */
263 newp->running = 0;
267 /* Enqueue the request in the request queue. */
268 if (newp != NULL)
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);
276 /* Release the mutex. */
277 pthread_mutex_unlock (&__gai_requests_mutex);
279 return newp;
283 static void *
284 __attribute__ ((noreturn))
285 handle_requests (void *arg)
287 struct requestlist *runp = (struct requestlist *) arg;
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
300 /* Make the request. */
301 struct gaicb *req = runp->gaicbp;
302 struct requestlist *srchp;
303 struct requestlist *lastp;
305 req->__return = getaddrinfo (req->ar_name, req->ar_service,
306 req->ar_request, &req->ar_result);
308 /* Get the mutex. */
309 pthread_mutex_lock (&__gai_requests_mutex);
311 /* Send the signal to notify about finished processing of the
312 request. */
313 __gai_notify (runp);
315 /* Now dequeue the current request. */
316 lastp = NULL;
317 srchp = requests;
318 while (srchp != runp)
320 lastp = srchp;
321 srchp = srchp->next;
323 assert (runp->running == 1);
325 if (requests_tail == runp)
326 requests_tail = lastp;
327 if (lastp == NULL)
328 requests = requests->next;
329 else
330 lastp->next = runp->next;
332 /* Free the old element. */
333 runp->next = freelist;
334 freelist = runp;
337 runp = requests;
338 while (runp != NULL && runp->running != 0)
339 runp = runp->next;
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)
345 struct timeval now;
346 struct timespec wakeup_time;
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)
354 wakeup_time.tv_nsec -= 1000000000;
355 ++wakeup_time.tv_sec;
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;
365 if (runp == NULL)
366 --nthreads;
367 else
369 /* Mark the request as being worked on. */
370 assert (runp->running == 0);
371 runp->running = 1;
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)
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)
386 pthread_t thid;
387 pthread_attr_t attr;
389 /* Make sure the thread is created detached. */
390 pthread_attr_init (&attr);
391 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
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;
403 /* Release the mutex. */
404 pthread_mutex_unlock (&__gai_requests_mutex);
406 while (runp != NULL);
408 pthread_exit (NULL);
412 /* Free allocated resources. */
413 libc_freeres_fn (free_res)
415 size_t row;
417 for (row = 0; row < pool_max_size; ++row)
418 free (pool[row]);
420 free (pool);