Add support for alternative waiting for completion.
[kopensolaris-gnu/glibc.git] / resolv / getaddrinfo_a.c
1 /* Copyright (C) 2001, 2006 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, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <errno.h>
21 #include <netdb.h>
22 #include <pthread.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25
26 #include <gai_misc.h>
27
28
29 /* We need this special structure to handle asynchronous I/O.  */
30 struct async_waitlist
31   {
32     int counter;
33     struct sigevent sigev;
34     struct waitlist list[0];
35   };
36
37
38 int
39 getaddrinfo_a (int mode, struct gaicb *list[], int ent, struct sigevent *sig)
40 {
41   struct sigevent defsigev;
42   struct requestlist *requests[ent];
43   int cnt;
44   volatile int total = 0;
45   int result = 0;
46
47   /* Check arguments.  */
48   if (mode != GAI_WAIT && mode != GAI_NOWAIT)
49     {
50       __set_errno (EINVAL);
51       return EAI_SYSTEM;
52     }
53
54   if (sig == NULL)
55     {
56       defsigev.sigev_notify = SIGEV_NONE;
57       sig = &defsigev;
58     }
59
60   /* Request the mutex.  */
61   pthread_mutex_lock (&__gai_requests_mutex);
62
63   /* Now we can enqueue all requests.  Since we already acquired the
64      mutex the enqueue function need not do this.  */
65   for (cnt = 0; cnt < ent; ++cnt)
66     if (list[cnt] != NULL)
67       {
68         requests[cnt] = __gai_enqueue_request (list[cnt]);
69
70         if (requests[cnt] != NULL)
71           /* Successfully enqueued.  */
72           ++total;
73         else
74           /* Signal that we've seen an error.  `errno' and the error code
75              of the gaicb will tell more.  */
76           result = EAI_SYSTEM;
77       }
78     else
79       requests[cnt] = NULL;
80
81   if (total == 0)
82     {
83       /* We don't have anything to do except signalling if we work
84          asynchronously.  */
85
86       /* Release the mutex.  We do this before raising a signal since the
87          signal handler might do a `siglongjmp' and then the mutex is
88          locked forever.  */
89       pthread_mutex_unlock (&__gai_requests_mutex);
90
91       if (mode == GAI_NOWAIT)
92         __gai_notify_only (sig,
93                            sig->sigev_notify == SIGEV_SIGNAL ? getpid () : 0);
94
95       return result;
96     }
97   else if (mode == GAI_WAIT)
98     {
99 #ifndef DONT_NEED_GAI_MISC_COND
100       pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
101 #endif
102       struct waitlist waitlist[ent];
103       int oldstate;
104
105       total = 0;
106       for (cnt = 0; cnt < ent; ++cnt)
107         if (requests[cnt] != NULL)
108           {
109 #ifndef DONT_NEED_GAI_MISC_COND
110             waitlist[cnt].cond = &cond;
111 #endif
112             waitlist[cnt].next = requests[cnt]->waiting;
113             waitlist[cnt].counterp = &total;
114             waitlist[cnt].sigevp = NULL;
115             waitlist[cnt].caller_pid = 0;       /* Not needed.  */
116             requests[cnt]->waiting = &waitlist[cnt];
117             ++total;
118           }
119
120       /* Since `pthread_cond_wait'/`pthread_cond_timedwait' are cancelation
121          points we must be careful.  We added entries to the waiting lists
122          which we must remove.  So defer cancelation for now.  */
123       pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &oldstate);
124
125       while (total > 0)
126         {
127 #ifdef DONT_NEED_GAI_MISC_COND
128           int result;
129           GAI_MISC_WAIT (result, total, NULL, 1);
130 #else
131           pthread_cond_wait (&cond, &__gai_requests_mutex);
132 #endif
133         }
134
135       /* Now it's time to restore the cancelation state.  */
136       pthread_setcancelstate (oldstate, NULL);
137
138 #ifndef DONT_NEED_GAI_MISC_COND
139       /* Release the conditional variable.  */
140       if (pthread_cond_destroy (&cond) != 0)
141         /* This must never happen.  */
142         abort ();
143 #endif
144     }
145   else
146     {
147       struct async_waitlist *waitlist;
148
149       waitlist = (struct async_waitlist *)
150         malloc (sizeof (struct async_waitlist)
151                 + (ent * sizeof (struct waitlist)));
152
153       if (waitlist == NULL)
154         result = EAI_AGAIN;
155       else
156         {
157           pid_t caller_pid = sig->sigev_notify == SIGEV_SIGNAL ? getpid () : 0;
158           total = 0;
159
160           for (cnt = 0; cnt < ent; ++cnt)
161             if (requests[cnt] != NULL)
162               {
163 #ifndef DONT_NEED_GAI_MISC_COND
164                 waitlist->list[cnt].cond = NULL;
165 #endif
166                 waitlist->list[cnt].next = requests[cnt]->waiting;
167                 waitlist->list[cnt].counterp = &waitlist->counter;
168                 waitlist->list[cnt].sigevp = &waitlist->sigev;
169                 waitlist->list[cnt].caller_pid = caller_pid;
170                 requests[cnt]->waiting = &waitlist->list[cnt];
171                 ++total;
172               }
173
174           waitlist->counter = total;
175           waitlist->sigev = *sig;
176         }
177     }
178
179   /* Release the mutex.  */
180   pthread_mutex_unlock (&__gai_requests_mutex);
181
182   return result;
183 }