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