]> sourceware.org Git - glibc.git/blob - resolv/gai_misc.c
Fix typo in recent resolver change which causes segvs
[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 }
This page took 0.065503 seconds and 5 git commands to generate.