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