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
32extern 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. */
51static struct requestlist **pool;
52
53/* Number of total and allocated pool entries. */
54static size_t pool_max_size;
55static 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. */
66static struct requestlist *freelist;
67
68/* Structure list of all currently processed requests. */
69static struct requestlist *requests;
70static struct requestlist *requests_tail;
71
72/* Number of threads currently running. */
73static int nthreads;
74
75/* Number of threads waiting for work to arrive. */
76static 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. */
81static 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. */
95pthread_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. */
100pthread_cond_t __gai_new_request_notification = PTHREAD_COND_INITIALIZER;
101
102
103/* Functions to handle request list pool. */
104static struct requestlist *
105get_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
154struct requestlist *
155internal_function
156__gai_find_request (const struct gaicb *gaicbp)
157{
158 struct requestlist *runp;
159
160 runp = requests;
161 while (runp != NULL)
162 if (runp->gaicbp == gaicbp)
163 return runp;
164 else
165 runp = runp->next;
166
167 return NULL;
168}
169
170
171int
172internal_function
173__gai_remove_request (struct gaicb *gaicbp)
174{
175 struct requestlist *runp;
176 struct requestlist *lastp;
177
178 runp = requests;
179 lastp = NULL;
180 while (runp != NULL)
181 if (runp->gaicbp == gaicbp)
182 break;
183 else
184 {
185 lastp = runp;
186 runp = runp->next;
187 }
188
189 if (runp == NULL)
190 /* Not known. */
191 return -1;
192 if (runp->running != 0)
193 /* Currently handled. */
194 return 1;
195
196 /* Dequeue the request. */
197 if (lastp == NULL)
198 requests = runp->next;
199 else
200 lastp->next = runp->next;
201 if (runp == requests_tail)
202 requests_tail = lastp;
203
204 return 0;
205}
206
207
208/* The thread handler. */
209static void *handle_requests (void *arg);
210
211
212/* The main function of the async I/O handling. It enqueues requests
213 and if necessary starts and handles threads. */
214struct requestlist *
215internal_function
216__gai_enqueue_request (struct gaicb *gaicbp)
217{
218 struct requestlist *newp;
219 struct requestlist *lastp;
220
221 /* Get the mutex. */
222 pthread_mutex_lock (&__gai_requests_mutex);
223
224 /* Get a new element for the waiting list. */
225 newp = get_elem ();
226 if (newp == NULL)
227 {
228 pthread_mutex_unlock (&__gai_requests_mutex);
229 __set_errno (EAGAIN);
230 return NULL;
231 }
232 newp->running = 0;
233 newp->gaicbp = gaicbp;
234 newp->waiting = NULL;
235 newp->next = NULL;
236
237 lastp = requests_tail;
238 if (requests_tail == NULL)
239 requests = requests_tail = newp;
240 else
241 {
242 requests_tail->next = newp;
243 requests_tail = newp;
244 }
245
246 gaicbp->__return = EAI_INPROGRESS;
247
248 /* See if we need to and are able to create a thread. */
249 if (nthreads < optim.gai_threads && idle_thread_count == 0)
250 {
251 pthread_t thid;
252
253 newp->running = 1;
254
255 /* Now try to start a thread. */
256 if (gai_create_helper_thread (&thid, handle_requests, newp) == 0)
257 /* We managed to enqueue the request. All errors which can
258 happen now can be recognized by calls to `gai_error'. */
259 ++nthreads;
260 else
261 {
262 if (nthreads == 0)
263 {
264 /* We cannot create a thread in the moment and there is
265 also no thread running. This is a problem. `errno' is
266 set to EAGAIN if this is only a temporary problem. */
267 assert (requests == newp || lastp->next == newp);
268 if (lastp != NULL)
269 lastp->next = NULL;
270 else
271 requests = NULL;
272 requests_tail = lastp;
273
274 newp->next = freelist;
275 freelist = newp;
276
277 newp = NULL;
278 }
279 else
280 /* We are not handling the request after all. */
281 newp->running = 0;
282 }
283 }
284
285 /* Enqueue the request in the request queue. */
286 if (newp != NULL)
287 {
288 /* If there is a thread waiting for work, then let it know that we
289 have just given it something to do. */
290 if (idle_thread_count > 0)
291 pthread_cond_signal (&__gai_new_request_notification);
292 }
293
294 /* Release the mutex. */
295 pthread_mutex_unlock (&__gai_requests_mutex);
296
297 return newp;
298}
299
300
301static void *
302__attribute__ ((noreturn))
303handle_requests (void *arg)
304{
305 struct requestlist *runp = (struct requestlist *) arg;
306
307 do
308 {
309 /* If runp is NULL, then we were created to service the work queue
310 in general, not to handle any particular request. In that case we
311 skip the "do work" stuff on the first pass, and go directly to the
312 "get work off the work queue" part of this loop, which is near the
313 end. */
314 if (runp == NULL)
315 pthread_mutex_lock (&__gai_requests_mutex);
316 else
317 {
318 /* Make the request. */
319 struct gaicb *req = runp->gaicbp;
320 struct requestlist *srchp;
321 struct requestlist *lastp;
322
323 req->__return = getaddrinfo (req->ar_name, req->ar_service,
324 req->ar_request, &req->ar_result);
325
326 /* Get the mutex. */
327 pthread_mutex_lock (&__gai_requests_mutex);
328
329 /* Send the signal to notify about finished processing of the
330 request. */
331 __gai_notify (runp);
332
333 /* Now dequeue the current request. */
334 lastp = NULL;
335 srchp = requests;
336 while (srchp != runp)
337 {
338 lastp = srchp;
339 srchp = srchp->next;
340 }
341 assert (runp->running == 1);
342
343 if (requests_tail == runp)
344 requests_tail = lastp;
345 if (lastp == NULL)
346 requests = requests->next;
347 else
348 lastp->next = runp->next;
349
350 /* Free the old element. */
351 runp->next = freelist;
352 freelist = runp;
353 }
354
355 runp = requests;
356 while (runp != NULL && runp->running != 0)
357 runp = runp->next;
358
359 /* If the runlist is empty, then we sleep for a while, waiting for
360 something to arrive in it. */
361 if (runp == NULL && optim.gai_idle_time >= 0)
362 {
363 struct timeval now;
364 struct timespec wakeup_time;
365
366 ++idle_thread_count;
367 gettimeofday (&now, NULL);
368 wakeup_time.tv_sec = now.tv_sec + optim.gai_idle_time;
369 wakeup_time.tv_nsec = now.tv_usec * 1000;
370 if (wakeup_time.tv_nsec >= 1000000000)
371 {
372 wakeup_time.tv_nsec -= 1000000000;
373 ++wakeup_time.tv_sec;
374 }
375 pthread_cond_timedwait (&__gai_new_request_notification,
376 &__gai_requests_mutex, &wakeup_time);
377 --idle_thread_count;
378 runp = requests;
379 while (runp != NULL && runp->running != 0)
380 runp = runp->next;
381 }
382
383 if (runp == NULL)
384 --nthreads;
385 else
386 {
387 /* Mark the request as being worked on. */
388 assert (runp->running == 0);
389 runp->running = 1;
390
391 /* If we have a request to process, and there's still another in
392 the run list, then we need to either wake up or create a new
393 thread to service the request that is still in the run list. */
394 if (requests != NULL)
395 {
396 /* There are at least two items in the work queue to work on.
397 If there are other idle threads, then we should wake them
398 up for these other work elements; otherwise, we should try
399 to create a new thread. */
400 if (idle_thread_count > 0)
401 pthread_cond_signal (&__gai_new_request_notification);
402 else if (nthreads < optim.gai_threads)
403 {
404 pthread_t thid;
405 pthread_attr_t attr;
406
407 /* Make sure the thread is created detached. */
408 pthread_attr_init (&attr);
409 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
410
411 /* Now try to start a thread. If we fail, no big deal,
412 because we know that there is at least one thread (us)
413 that is working on lookup operations. */
414 if (pthread_create (&thid, &attr, handle_requests, NULL)
415 == 0)
416 ++nthreads;
417 }
418 }
419 }
420
421 /* Release the mutex. */
422 pthread_mutex_unlock (&__gai_requests_mutex);
423 }
424 while (runp != NULL);
425
426 pthread_exit (NULL);
427}
428
429
430/* Free allocated resources. */
431libc_freeres_fn (free_res)
432{
433 size_t row;
434
435 for (row = 0; row < pool_max_size; ++row)
436 free (pool[row]);
437
438 free (pool);
439}
440