1 /* Copyright (C) 2001 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 Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 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 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
30 /* Pool of request list entries. */
31 static struct requestlist
**pool
;
33 /* Number of total and allocated pool entries. */
34 static size_t pool_max_size
;
35 static size_t pool_size
;
37 /* We implement a two dimensional array but allocate each row separately.
38 The macro below determines how many entries should be used per row.
39 It should better be a power of two. */
40 #define ENTRIES_PER_ROW 32
42 /* How many rows we allocate at once. */
45 /* List of available entries. */
46 static struct requestlist
*freelist
;
48 /* Structure list of all currently processed requests. */
49 static struct requestlist
*requests
;
50 static struct requestlist
*requests_tail
;
52 /* Number of threads currently running. */
55 /* Number of threads waiting for work to arrive. */
56 static int idle_thread_count
;
59 /* These are the values used for optimization. We will probably
60 create a funcion to set these values. */
61 static struct gaiinit optim
=
63 20, /* int gai_threads; Maximal number of threads. */
64 64, /* int gai_num; Number of expected simultanious requests. */
74 /* Since the list is global we need a mutex protecting it. */
75 pthread_mutex_t __gai_requests_mutex
= PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
;
77 /* When you add a request to the list and there are idle threads present,
78 you signal this condition variable. When a thread finishes work, it waits
79 on this condition variable for a time before it actually exits. */
80 pthread_cond_t __gai_new_request_notification
= PTHREAD_COND_INITIALIZER
;
83 /* Functions to handle request list pool. */
84 static struct requestlist
*
87 struct requestlist
*result
;
91 struct requestlist
*new_row
;
94 if (pool_size
+ 1 >= pool_max_size
)
96 size_t new_max_size
= pool_max_size
+ ROWS_STEP
;
97 struct requestlist
**new_tab
;
99 new_tab
= (struct requestlist
**)
100 realloc (pool
, new_max_size
* sizeof (struct requestlist
*));
105 pool_max_size
= new_max_size
;
109 /* Allocate the new row. */
110 cnt
= pool_size
== 0 ? optim
.gai_num
: ENTRIES_PER_ROW
;
111 new_row
= (struct requestlist
*) calloc (cnt
,
112 sizeof (struct requestlist
));
116 pool
[pool_size
++] = new_row
;
118 /* Put all the new entries in the freelist. */
121 new_row
->next
= freelist
;
122 freelist
= new_row
++;
128 freelist
= freelist
->next
;
136 __gai_find_request (const struct gaicb
*gaicbp
)
138 struct requestlist
*runp
;
142 if (runp
->gaicbp
== gaicbp
)
153 __gai_remove_request (struct gaicb
*gaicbp
)
155 struct requestlist
*runp
;
156 struct requestlist
*lastp
;
161 if (runp
->gaicbp
== gaicbp
)
172 if (runp
->running
!= 0)
173 /* Currently handled. */
176 /* Dequeue the request. */
178 requests
= runp
->next
;
180 lastp
->next
= runp
->next
;
181 if (runp
== requests_tail
)
182 requests_tail
= lastp
;
188 /* The thread handler. */
189 static void *handle_requests (void *arg
);
192 /* The main function of the async I/O handling. It enqueues requests
193 and if necessary starts and handles threads. */
196 __gai_enqueue_request (struct gaicb
*gaicbp
)
198 struct requestlist
*newp
;
199 struct requestlist
*lastp
;
202 pthread_mutex_lock (&__gai_requests_mutex
);
204 /* Get a new element for the waiting list. */
208 pthread_mutex_unlock (&__gai_requests_mutex
);
209 __set_errno (EAGAIN
);
213 newp
->gaicbp
= gaicbp
;
214 newp
->waiting
= NULL
;
217 lastp
= requests_tail
;
218 if (requests_tail
== NULL
)
219 requests
= requests_tail
= newp
;
222 requests_tail
->next
= newp
;
223 requests_tail
= newp
;
226 gaicbp
->__return
= EAI_INPROGRESS
;
228 /* See if we need to and are able to create a thread. */
229 if (nthreads
< optim
.gai_threads
&& idle_thread_count
== 0)
236 /* Make sure the thread is created detached. */
237 pthread_attr_init (&attr
);
238 pthread_attr_setdetachstate (&attr
, PTHREAD_CREATE_DETACHED
);
240 /* Now try to start a thread. */
241 if (pthread_create (&thid
, &attr
, handle_requests
, newp
) == 0)
242 /* We managed to enqueue the request. All errors which can
243 happen now can be recognized by calls to `gai_error'. */
249 /* We cannot create a thread in the moment and there is
250 also no thread running. This is a problem. `errno' is
251 set to EAGAIN if this is only a temporary problem. */
252 assert (lastp
->next
== newp
);
254 requests_tail
= lastp
;
256 newp
->next
= freelist
;
262 /* We are not handling the request after all. */
267 /* Enqueue the request in the request queue. */
270 /* If there is a thread waiting for work, then let it know that we
271 have just given it something to do. */
272 if (idle_thread_count
> 0)
273 pthread_cond_signal (&__gai_new_request_notification
);
276 /* Release the mutex. */
277 pthread_mutex_unlock (&__gai_requests_mutex
);
284 __attribute__ ((noreturn
))
285 handle_requests (void *arg
)
287 struct requestlist
*runp
= (struct requestlist
*) arg
;
291 /* If runp is NULL, then we were created to service the work queue
292 in general, not to handle any particular request. In that case we
293 skip the "do work" stuff on the first pass, and go directly to the
294 "get work off the work queue" part of this loop, which is near the
297 pthread_mutex_lock (&__gai_requests_mutex
);
300 /* Make the request. */
301 struct gaicb
*req
= runp
->gaicbp
;
302 struct requestlist
*srchp
;
303 struct requestlist
*lastp
;
305 req
->__return
= getaddrinfo (req
->ar_name
, req
->ar_service
,
306 req
->ar_request
, &req
->ar_result
);
309 pthread_mutex_lock (&__gai_requests_mutex
);
311 /* Send the signal to notify about finished processing of the
315 /* Now dequeue the current request. */
318 while (srchp
!= runp
)
323 assert (runp
->running
== 1);
325 if (requests_tail
== runp
)
326 requests_tail
= lastp
;
328 requests
= requests
->next
;
330 lastp
->next
= runp
->next
;
332 /* Free the old element. */
333 runp
->next
= freelist
;
338 while (runp
!= NULL
&& runp
->running
!= 0)
341 /* If the runlist is empty, then we sleep for a while, waiting for
342 something to arrive in it. */
343 if (runp
== NULL
&& optim
.gai_idle_time
>= 0)
346 struct timespec wakeup_time
;
349 gettimeofday (&now
, NULL
);
350 wakeup_time
.tv_sec
= now
.tv_sec
+ optim
.gai_idle_time
;
351 wakeup_time
.tv_nsec
= now
.tv_usec
* 1000;
352 if (wakeup_time
.tv_nsec
> 1000000000)
354 wakeup_time
.tv_nsec
-= 1000000000;
355 ++wakeup_time
.tv_sec
;
357 pthread_cond_timedwait (&__gai_new_request_notification
,
358 &__gai_requests_mutex
, &wakeup_time
);
361 while (runp
!= NULL
&& runp
->running
!= 0)
369 /* Mark the request as being worked on. */
370 assert (runp
->running
== 0);
373 /* If we have a request to process, and there's still another in
374 the run list, then we need to either wake up or create a new
375 thread to service the request that is still in the run list. */
376 if (requests
!= NULL
)
378 /* There are at least two items in the work queue to work on.
379 If there are other idle threads, then we should wake them
380 up for these other work elements; otherwise, we should try
381 to create a new thread. */
382 if (idle_thread_count
> 0)
383 pthread_cond_signal (&__gai_new_request_notification
);
384 else if (nthreads
< optim
.gai_threads
)
389 /* Make sure the thread is created detached. */
390 pthread_attr_init (&attr
);
391 pthread_attr_setdetachstate (&attr
, PTHREAD_CREATE_DETACHED
);
393 /* Now try to start a thread. If we fail, no big deal,
394 because we know that there is at least one thread (us)
395 that is working on lookup operations. */
396 if (pthread_create (&thid
, &attr
, handle_requests
, NULL
)
403 /* Release the mutex. */
404 pthread_mutex_unlock (&__gai_requests_mutex
);
406 while (runp
!= NULL
);
412 /* Free allocated resources. */
414 __attribute__ ((unused
))
419 for (row
= 0; row
< pool_max_size
; ++row
)
424 text_set_element (__libc_subfreeres
, free_res
);