Merge remote-tracking branch 'nsz/math'
[musl] / src / aio / lio_listio.c
1 #include <aio.h>
2 #include <errno.h>
3 #include <limits.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include "pthread_impl.h"
7
8 struct lio_state {
9         struct sigevent *sev;
10         int cnt;
11         struct aiocb *cbs[];
12 };
13
14 static int lio_wait(struct lio_state *st)
15 {
16         int i, err, got_err;
17         int cnt = st->cnt;
18         struct aiocb **cbs = st->cbs;
19
20         for (;;) {
21                 for (i=0; i<cnt; i++) {
22                         if (!cbs[i]) continue;
23                         err = aio_error(cbs[i]);
24                         if (err==EINPROGRESS)
25                                 break;
26                         if (err) got_err=1;
27                         cbs[i] = 0;
28                 }
29                 if (i==cnt) {
30                         if (got_err) {
31                                 errno = EIO;
32                                 return -1;
33                         }
34                         return 0;
35                 }
36                 if (aio_suspend((void *)cbs, cnt, 0))
37                         return -1;
38         }
39 }
40
41 static void notify_signal(struct sigevent *sev)
42 {
43         siginfo_t si = {
44                 .si_signo = sev->sigev_signo,
45                 .si_value = sev->sigev_value,
46                 .si_code = SI_ASYNCIO,
47                 .si_pid = __pthread_self()->pid,
48                 .si_uid = getuid()
49         };
50         __syscall(SYS_rt_sigqueueinfo, si.si_pid, si.si_signo, &si);
51 }
52
53 static void *wait_thread(void *p)
54 {
55         struct lio_state *st = p;
56         struct sigevent *sev = st->sev;
57         lio_wait(st);
58         free(st);
59         switch (sev->sigev_notify) {
60         case SIGEV_SIGNAL:
61                 notify_signal(sev);
62                 break;
63         case SIGEV_THREAD:
64                 sev->sigev_notify_function(sev->sigev_value);
65                 break;
66         }
67         return 0;
68 }
69
70 int lio_listio(int mode, struct aiocb *restrict const *restrict cbs, int cnt, struct sigevent *restrict sev)
71 {
72         int i, ret;
73         struct lio_state *st=0;
74
75         if (cnt < 0) {
76                 errno = EINVAL;
77                 return -1;
78         }
79
80         if (mode == LIO_WAIT || (sev && sev->sigev_notify != SIGEV_NONE)) {
81                 if (!(st = malloc(sizeof *st + cnt*sizeof *cbs))) {
82                         errno = EAGAIN;
83                         return -1;
84                 }
85                 st->cnt = cnt;
86                 st->sev = sev;
87                 memcpy(st->cbs, (void*) cbs, cnt*sizeof *cbs);
88         }
89
90         for (i=0; i<cnt; i++) {
91                 if (!cbs[i]) continue;
92                 switch (cbs[i]->aio_lio_opcode) {
93                 case LIO_READ:
94                         ret = aio_read(cbs[i]);
95                         break;
96                 case LIO_WRITE:
97                         ret = aio_write(cbs[i]);
98                         break;
99                 default:
100                         continue;
101                 }
102                 if (ret) {
103                         free(st);
104                         errno = EAGAIN;
105                         return -1;
106                 }
107         }
108
109         if (mode == LIO_WAIT) {
110                 ret = lio_wait(st);
111                 free(st);
112                 return ret;
113         }
114
115         if (st) {
116                 pthread_attr_t a;
117                 sigset_t set;
118                 pthread_t td;
119
120                 if (sev->sigev_notify == SIGEV_THREAD) {
121                         if (sev->sigev_notify_attributes)
122                                 a = *sev->sigev_notify_attributes;
123                         else
124                                 pthread_attr_init(&a);
125                 } else {
126                         pthread_attr_init(&a);
127                         pthread_attr_setstacksize(&a, PAGE_SIZE);
128                         pthread_attr_setguardsize(&a, 0);
129                 }
130                 pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
131                 sigfillset(&set);
132                 pthread_sigmask(SIG_BLOCK, &set, &set);
133                 if (pthread_create(&td, &a, wait_thread, st)) {
134                         free(st);
135                         errno = EAGAIN;
136                         return -1;
137                 }
138                 pthread_sigmask(SIG_SETMASK, &set, 0);
139         }
140
141         return 0;
142 }
143