2.5-18.1
[jlayton/glibc.git] / rtkaio / sysdeps / unix / sysv / linux / kaio_suspend.c
1 /* Suspend until termination of a requests.
2    Copyright (C) 1997, 1998, 1999, 2000, 2002, 2003, 2006
3    Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
5    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
6
7    The GNU C Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11
12    The GNU C Library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with the GNU C Library; if not, write to the Free
19    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20    02111-1307 USA.  */
21
22
23 /* We use an UGLY hack to prevent gcc from finding us cheating.  The
24    implementations of aio_suspend and aio_suspend64 are identical and so
25    we want to avoid code duplication by using aliases.  But gcc sees
26    the different parameter lists and prints a warning.  We define here
27    a function so that aio_suspend64 has no prototype.  */
28 #define aio_suspend64 XXX
29 #include <aio.h>
30 /* And undo the hack.  */
31 #undef aio_suspend64
32
33 #include <kaio_misc.h>
34
35 #ifndef USE_KAIO
36 #include <aio_suspend.c>
37 #else
38
39 #include <assert.h>
40 #include <errno.h>
41 #include <stdbool.h>
42 #include <stdlib.h>
43 #include <sys/time.h>
44
45 #include <bits/libc-lock.h>
46 #include <sysdep-cancel.h>
47
48
49 struct clparam
50 {
51   const struct aiocb *const *list;
52   struct waitlist *waitlist;
53   struct requestlist **requestlist;
54 #ifndef DONT_NEED_AIO_MISC_COND
55   pthread_cond_t *cond;
56 #endif
57   int nent;
58 };
59
60
61 static void
62 cleanup (void *arg)
63 {
64 #ifdef DONT_NEED_AIO_MISC_COND
65   /* Acquire the mutex.  If pthread_cond_*wait is used this would
66      happen implicitly.  */
67   pthread_mutex_lock (&__aio_requests_mutex);
68 #endif
69
70   const struct clparam *param = (const struct clparam *) arg;
71
72   /* Now remove the entry in the waiting list for all requests
73      which didn't terminate.  */
74   int cnt = param->nent;
75   while (cnt-- > 0)
76     if (param->list[cnt] != NULL
77         && param->list[cnt]->__error_code == EINPROGRESS)
78       {
79         struct waitlist **listp;
80
81         assert (param->requestlist[cnt] != NULL);
82
83         /* There is the chance that we cannot find our entry anymore. This
84            could happen if the request terminated and restarted again.  */
85         listp = &param->requestlist[cnt]->waiting;
86         while (*listp != NULL && *listp != &param->waitlist[cnt])
87           listp = &(*listp)->next;
88
89         if (*listp != NULL)
90           *listp = (*listp)->next;
91       }
92
93 #ifndef DONT_NEED_AIO_MISC_COND
94   /* Release the conditional variable.  */
95   (void) pthread_cond_destroy (param->cond);
96 #endif
97
98   /* Release the mutex.  */
99   pthread_mutex_unlock (&__aio_requests_mutex);
100 }
101
102
103 int
104 aio_suspend (list, nent, timeout)
105      const struct aiocb *const list[];
106      int nent;
107      const struct timespec *timeout;
108 {
109   if (__builtin_expect (nent < 0, 0))
110     {
111       __set_errno (EINVAL);
112       return -1;
113     }
114
115   struct waitlist waitlist[nent];
116   struct requestlist *requestlist[nent];
117 #ifndef DONT_NEED_AIO_MISC_COND
118   pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
119 #endif
120   int cnt;
121   int result = 0;
122   int cntr = 1;
123   int total = 0, ktotal = 0;
124
125   /* Request the mutex.  */
126   pthread_mutex_lock (&__aio_requests_mutex);
127
128   /* There is not yet a finished request.  Signal the request that
129      we are working for it.  */
130   for (cnt = 0; cnt < nent; ++cnt)
131     if (list[cnt] != NULL)
132       {
133         if (list[cnt]->__error_code == EINPROGRESS)
134           {
135             requestlist[cnt] = __aio_find_req ((aiocb_union *) list[cnt]);
136
137             if (requestlist[cnt] != NULL)
138               {
139 #ifndef DONT_NEED_AIO_MISC_COND
140                 waitlist[cnt].cond = &cond;
141 #endif
142                 waitlist[cnt].result = NULL;
143                 waitlist[cnt].next = requestlist[cnt]->waiting;
144                 waitlist[cnt].counterp = &cntr;
145                 waitlist[cnt].sigevp = NULL;
146 #ifdef BROKEN_THREAD_SIGNALS
147                 waitlist[cnt].caller_pid = 0;   /* Not needed.  */
148 #endif
149                 requestlist[cnt]->waiting = &waitlist[cnt];
150                 total++;
151                 if (requestlist[cnt]->kioctx != KCTX_NONE)
152                   ktotal++;
153               }
154             else
155               /* We will never suspend.  */
156               break;
157           }
158         else
159           /* We will never suspend.  */
160           break;
161       }
162
163
164   /* Only if none of the entries is NULL or finished to be wait.  */
165   if (cnt == nent && total)
166     {
167       struct clparam clparam =
168         {
169           .list = list,
170           .waitlist = waitlist,
171           .requestlist = requestlist,
172 #ifndef DONT_NEED_AIO_MISC_COND
173           .cond = &cond,
174 #endif
175           .nent = nent
176         };
177
178       pthread_cleanup_push (cleanup, &clparam);
179
180       if (!__kernel_thread_started && ktotal)
181         {
182           /* If the kernel aio thread was not started yet all requests
183              are served by the kernel and there are no other threads running,
184              read events with mutex hold, so that nobody else can get them
185              instead of us here.  */
186           if (SINGLE_THREAD_P && total == ktotal)
187             {
188               if (timeout == NULL)
189                 {
190                   while (cntr == 1)
191                     __aio_wait_for_events (__aio_kioctx, NULL);
192                 }
193               else
194                 {
195                   struct timeval now;
196                   struct timespec abstime, ts;
197
198                   __gettimeofday (&now, NULL);
199                   abstime.tv_nsec = timeout->tv_nsec + now.tv_usec * 1000;
200                   abstime.tv_sec = timeout->tv_sec + now.tv_sec;
201                   if (abstime.tv_nsec >= 1000000000)
202                     {
203                       abstime.tv_nsec -= 1000000000;
204                       abstime.tv_sec += 1;
205                     }
206
207                   for (;;)
208                     {
209                       result = __aio_wait_for_events (__aio_kioctx, timeout);
210                       if (cntr < 1)
211                         break;
212                       if (result == ETIMEDOUT)
213                         break;
214
215                       __gettimeofday (&now, NULL);
216                       if (now.tv_sec > abstime.tv_sec
217                           || (now.tv_sec == abstime.tv_sec
218                               && now.tv_usec * 1000 >= abstime.tv_nsec))
219                         break;
220
221                       ts.tv_nsec = abstime.tv_nsec - now.tv_usec * 1000;
222                       ts.tv_sec = abstime.tv_sec - now.tv_sec;
223                       if (abstime.tv_nsec < now.tv_usec * 1000)
224                         {
225                           ts.tv_nsec += 1000000000;
226                           ts.tv_sec -= 1;
227                         }
228                       timeout = &ts;
229                     }
230
231                   if (cntr < 1)
232                     result = 0;
233                   else
234                     result = ETIMEDOUT;
235                 }
236               total = 0;
237             }
238           else if (__aio_create_kernel_thread () < 0)
239             {
240               total = 0;
241               __set_errno (ENOMEM);
242               result = -1;
243             }
244         }
245
246       if (total == 0)
247         /* Suspending was handled above.  */
248         ;
249 #ifdef DONT_NEED_AIO_MISC_COND
250       else
251         AIO_MISC_WAIT (result, cntr, timeout, 1);
252 #else
253       else if (timeout == NULL)
254         result = pthread_cond_wait (&cond, &__aio_requests_mutex);
255       else
256         {
257           /* We have to convert the relative timeout value into an
258              absolute time value with pthread_cond_timedwait expects.  */
259           struct timeval now;
260           struct timespec abstime;
261
262           __gettimeofday (&now, NULL);
263           abstime.tv_nsec = timeout->tv_nsec + now.tv_usec * 1000;
264           abstime.tv_sec = timeout->tv_sec + now.tv_sec;
265           if (abstime.tv_nsec >= 1000000000)
266             {
267               abstime.tv_nsec -= 1000000000;
268               abstime.tv_sec += 1;
269             }
270
271           result = pthread_cond_timedwait (&cond, &__aio_requests_mutex,
272                                            &abstime);
273         }
274 #endif
275
276       pthread_cleanup_pop (0);
277     }
278
279   /* Now remove the entry in the waiting list for all requests
280      which didn't terminate.  */
281   while (cnt-- > 0)
282     if (list[cnt] != NULL && list[cnt]->__error_code == EINPROGRESS)
283       {
284         struct waitlist **listp;
285
286         assert (requestlist[cnt] != NULL);
287
288         /* There is the chance that we cannot find our entry anymore. This
289            could happen if the request terminated and restarted again.  */
290         listp = &requestlist[cnt]->waiting;
291         while (*listp != NULL && *listp != &waitlist[cnt])
292           listp = &(*listp)->next;
293
294         if (*listp != NULL)
295           *listp = (*listp)->next;
296       }
297
298 #ifndef DONT_NEED_AIO_MISC_COND
299   /* Release the conditional variable.  */
300   if (__builtin_expect (pthread_cond_destroy (&cond) != 0, 0))
301     /* This must never happen.  */
302     abort ();
303 #endif
304
305   if (result != 0)
306     {
307 #ifndef DONT_NEED_AIO_MISC_COND
308       /* An error occurred.  Possibly it's ETIMEDOUT.  We have to translate
309          the timeout error report of `pthread_cond_timedwait' to the
310          form expected from `aio_suspend'.  */
311       if (result == ETIMEDOUT)
312         __set_errno (EAGAIN);
313       else
314 #endif
315         __set_errno (result);
316
317       result = -1;
318     }
319
320   /* Release the mutex.  */
321   pthread_mutex_unlock (&__aio_requests_mutex);
322
323   return result;
324 }
325
326 weak_alias (aio_suspend, aio_suspend64)
327 #endif