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 | } |
447 | |