]> sourceware.org Git - glibc.git/blob - resolv/gai_misc.c
* include/libc-symbols.h (__libc_freeres_fn_section, libc_freeres_fn):
[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 }
This page took 0.063131 seconds and 6 git commands to generate.