Update old tunables framework document/script.
[glibc.git] / resolv / gai_misc.c
blobfd9aa356be6a94f27346160cb23e55d618ca3efd
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.
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.
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.
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/>. */
19 #include <assert.h>
20 #include <errno.h>
21 #include <pthread.h>
22 #include <stdlib.h>
23 #include <sys/time.h>
25 #include <gai_misc.h>
29 #ifndef gai_create_helper_thread
30 # define gai_create_helper_thread __gai_create_helper_thread
32 extern inline int
33 __gai_create_helper_thread (pthread_t *threadp, void *(*tf) (void *),
34 void *arg)
36 pthread_attr_t attr;
38 /* Make sure the thread is created detached. */
39 pthread_attr_init (&attr);
40 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
42 int ret = pthread_create (threadp, &attr, tf, arg);
44 (void) pthread_attr_destroy (&attr);
45 return ret;
47 #endif
50 /* Pool of request list entries. */
51 static struct requestlist **pool;
53 /* Number of total and allocated pool entries. */
54 static size_t pool_max_size;
55 static size_t pool_size;
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
62 /* How many rows we allocate at once. */
63 #define ROWS_STEP 8
65 /* List of available entries. */
66 static struct requestlist *freelist;
68 /* Structure list of all currently processed requests. */
69 static struct requestlist *requests;
70 static struct requestlist *requests_tail;
72 /* Number of threads currently running. */
73 static int nthreads;
75 /* Number of threads waiting for work to arrive. */
76 static int idle_thread_count;
79 /* These are the values used for optimization. We will probably
80 create a funcion to set these values. */
81 static struct gaiinit optim =
83 20, /* int gai_threads; Maximal number of threads. */
84 64, /* int gai_num; Number of expected simultanious requests. */
94 /* Since the list is global we need a mutex protecting it. */
95 pthread_mutex_t __gai_requests_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
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;
103 /* Functions to handle request list pool. */
104 static struct requestlist *
105 get_elem (void)
107 struct requestlist *result;
109 if (freelist == NULL)
111 struct requestlist *new_row;
112 int cnt;
114 if (pool_size + 1 >= pool_max_size)
116 size_t new_max_size = pool_max_size + ROWS_STEP;
117 struct requestlist **new_tab;
119 new_tab = (struct requestlist **)
120 realloc (pool, new_max_size * sizeof (struct requestlist *));
122 if (new_tab == NULL)
123 return NULL;
125 pool_max_size = new_max_size;
126 pool = new_tab;
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;
136 pool[pool_size++] = new_row;
138 /* Put all the new entries in the freelist. */
141 new_row->next = freelist;
142 freelist = new_row++;
144 while (--cnt > 0);
147 result = freelist;
148 freelist = freelist->next;
150 return result;
154 struct requestlist *
155 internal_function
156 __gai_find_request (const struct gaicb *gaicbp)
158 struct requestlist *runp;
160 runp = requests;
161 while (runp != NULL)
162 if (runp->gaicbp == gaicbp)
163 return runp;
164 else
165 runp = runp->next;
167 return NULL;
172 internal_function
173 __gai_remove_request (struct gaicb *gaicbp)
175 struct requestlist *runp;
176 struct requestlist *lastp;
178 runp = requests;
179 lastp = NULL;
180 while (runp != NULL)
181 if (runp->gaicbp == gaicbp)
182 break;
183 else
185 lastp = runp;
186 runp = runp->next;
189 if (runp == NULL)
190 /* Not known. */
191 return -1;
192 if (runp->running != 0)
193 /* Currently handled. */
194 return 1;
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;
204 return 0;
208 /* The thread handler. */
209 static void *handle_requests (void *arg);
212 /* The main function of the async I/O handling. It enqueues requests
213 and if necessary starts and handles threads. */
214 struct requestlist *
215 internal_function
216 __gai_enqueue_request (struct gaicb *gaicbp)
218 struct requestlist *newp;
219 struct requestlist *lastp;
221 /* Get the mutex. */
222 pthread_mutex_lock (&__gai_requests_mutex);
224 /* Get a new element for the waiting list. */
225 newp = get_elem ();
226 if (newp == NULL)
228 pthread_mutex_unlock (&__gai_requests_mutex);
229 __set_errno (EAGAIN);
230 return NULL;
232 newp->running = 0;
233 newp->gaicbp = gaicbp;
234 newp->waiting = NULL;
235 newp->next = NULL;
237 lastp = requests_tail;
238 if (requests_tail == NULL)
239 requests = requests_tail = newp;
240 else
242 requests_tail->next = newp;
243 requests_tail = newp;
246 gaicbp->__return = EAI_INPROGRESS;
248 /* See if we need to and are able to create a thread. */
249 if (nthreads < optim.gai_threads && idle_thread_count == 0)
251 pthread_t thid;
253 newp->running = 1;
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
262 if (nthreads == 0)
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 (lastp->next == newp);
268 lastp->next = NULL;
269 requests_tail = lastp;
271 newp->next = freelist;
272 freelist = newp;
274 newp = NULL;
276 else
277 /* We are not handling the request after all. */
278 newp->running = 0;
282 /* Enqueue the request in the request queue. */
283 if (newp != NULL)
285 /* If there is a thread waiting for work, then let it know that we
286 have just given it something to do. */
287 if (idle_thread_count > 0)
288 pthread_cond_signal (&__gai_new_request_notification);
291 /* Release the mutex. */
292 pthread_mutex_unlock (&__gai_requests_mutex);
294 return newp;
298 static void *
299 __attribute__ ((noreturn))
300 handle_requests (void *arg)
302 struct requestlist *runp = (struct requestlist *) arg;
306 /* If runp is NULL, then we were created to service the work queue
307 in general, not to handle any particular request. In that case we
308 skip the "do work" stuff on the first pass, and go directly to the
309 "get work off the work queue" part of this loop, which is near the
310 end. */
311 if (runp == NULL)
312 pthread_mutex_lock (&__gai_requests_mutex);
313 else
315 /* Make the request. */
316 struct gaicb *req = runp->gaicbp;
317 struct requestlist *srchp;
318 struct requestlist *lastp;
320 req->__return = getaddrinfo (req->ar_name, req->ar_service,
321 req->ar_request, &req->ar_result);
323 /* Get the mutex. */
324 pthread_mutex_lock (&__gai_requests_mutex);
326 /* Send the signal to notify about finished processing of the
327 request. */
328 __gai_notify (runp);
330 /* Now dequeue the current request. */
331 lastp = NULL;
332 srchp = requests;
333 while (srchp != runp)
335 lastp = srchp;
336 srchp = srchp->next;
338 assert (runp->running == 1);
340 if (requests_tail == runp)
341 requests_tail = lastp;
342 if (lastp == NULL)
343 requests = requests->next;
344 else
345 lastp->next = runp->next;
347 /* Free the old element. */
348 runp->next = freelist;
349 freelist = runp;
352 runp = requests;
353 while (runp != NULL && runp->running != 0)
354 runp = runp->next;
356 /* If the runlist is empty, then we sleep for a while, waiting for
357 something to arrive in it. */
358 if (runp == NULL && optim.gai_idle_time >= 0)
360 struct timeval now;
361 struct timespec wakeup_time;
363 ++idle_thread_count;
364 gettimeofday (&now, NULL);
365 wakeup_time.tv_sec = now.tv_sec + optim.gai_idle_time;
366 wakeup_time.tv_nsec = now.tv_usec * 1000;
367 if (wakeup_time.tv_nsec >= 1000000000)
369 wakeup_time.tv_nsec -= 1000000000;
370 ++wakeup_time.tv_sec;
372 pthread_cond_timedwait (&__gai_new_request_notification,
373 &__gai_requests_mutex, &wakeup_time);
374 --idle_thread_count;
375 runp = requests;
376 while (runp != NULL && runp->running != 0)
377 runp = runp->next;
380 if (runp == NULL)
381 --nthreads;
382 else
384 /* Mark the request as being worked on. */
385 assert (runp->running == 0);
386 runp->running = 1;
388 /* If we have a request to process, and there's still another in
389 the run list, then we need to either wake up or create a new
390 thread to service the request that is still in the run list. */
391 if (requests != NULL)
393 /* There are at least two items in the work queue to work on.
394 If there are other idle threads, then we should wake them
395 up for these other work elements; otherwise, we should try
396 to create a new thread. */
397 if (idle_thread_count > 0)
398 pthread_cond_signal (&__gai_new_request_notification);
399 else if (nthreads < optim.gai_threads)
401 pthread_t thid;
402 pthread_attr_t attr;
404 /* Make sure the thread is created detached. */
405 pthread_attr_init (&attr);
406 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
408 /* Now try to start a thread. If we fail, no big deal,
409 because we know that there is at least one thread (us)
410 that is working on lookup operations. */
411 if (pthread_create (&thid, &attr, handle_requests, NULL)
412 == 0)
413 ++nthreads;
418 /* Release the mutex. */
419 pthread_mutex_unlock (&__gai_requests_mutex);
421 while (runp != NULL);
423 pthread_exit (NULL);
427 /* Free allocated resources. */
428 libc_freeres_fn (free_res)
430 size_t row;
432 for (row = 0; row < pool_max_size; ++row)
433 free (pool[row]);
435 free (pool);