]> git.ozlabs.org Git - ccan/blob - ccan/io/poll.c
ccan/io: rewrite.
[ccan] / ccan / io / poll.c
1 /* Licensed under LGPLv2.1+ - see LICENSE file for details */
2 #include "io.h"
3 #include "backend.h"
4 #include <assert.h>
5 #include <poll.h>
6 #include <stdlib.h>
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <limits.h>
10 #include <errno.h>
11
12 static size_t num_fds = 0, max_fds = 0, num_waiting = 0;
13 static struct pollfd *pollfds = NULL;
14 static struct fd **fds = NULL;
15 static struct io_conn *closing = NULL, *always = NULL;
16
17 static bool add_fd(struct fd *fd, short events)
18 {
19         if (!max_fds) {
20                 assert(num_fds == 0);
21                 pollfds = tal_arr(NULL, struct pollfd, 8);
22                 if (!pollfds)
23                         return false;
24                 fds = tal_arr(pollfds, struct fd *, 8);
25                 if (!fds)
26                         return false;
27                 max_fds = 8;
28         }
29
30         if (num_fds + 1 > max_fds) {
31                 size_t num = max_fds * 2;
32
33                 if (!tal_resize(&pollfds, num))
34                         return false;
35                 if (!tal_resize(&fds, num))
36                         return false;
37                 max_fds = num;
38         }
39
40         pollfds[num_fds].events = events;
41         /* In case it's idle. */
42         if (!events)
43                 pollfds[num_fds].fd = -fd->fd;
44         else
45                 pollfds[num_fds].fd = fd->fd;
46         pollfds[num_fds].revents = 0; /* In case we're iterating now */
47         fds[num_fds] = fd;
48         fd->backend_info = num_fds;
49         num_fds++;
50         if (events)
51                 num_waiting++;
52
53         return true;
54 }
55
56 static void del_fd(struct fd *fd)
57 {
58         size_t n = fd->backend_info;
59
60         assert(n != -1);
61         assert(n < num_fds);
62         if (pollfds[n].events)
63                 num_waiting--;
64         if (n != num_fds - 1) {
65                 /* Move last one over us. */
66                 pollfds[n] = pollfds[num_fds-1];
67                 fds[n] = fds[num_fds-1];
68                 assert(fds[n]->backend_info == num_fds-1);
69                 fds[n]->backend_info = n;
70         } else if (num_fds == 1) {
71                 /* Free everything when no more fds. */
72                 pollfds = tal_free(pollfds);
73                 fds = NULL;
74                 max_fds = 0;
75         }
76         num_fds--;
77         fd->backend_info = -1;
78         close(fd->fd);
79 }
80
81 bool add_listener(struct io_listener *l)
82 {
83         if (!add_fd(&l->fd, POLLIN))
84                 return false;
85         return true;
86 }
87
88 void backend_new_closing(struct io_conn *conn)
89 {
90         /* Already on always list?  Remove it. */
91         if (conn->list) {
92                 struct io_conn **p = &always;
93
94                 while (*p != conn)
95                         p = &(*p)->list;
96
97                 *p = conn->list;
98         }
99
100         conn->list = closing;
101         closing = conn;
102 }
103
104 void backend_new_always(struct io_conn *conn)
105 {
106         /* May already be in always list (other plan), or closing. */
107         if (!conn->list) {
108                 conn->list = always;
109                 always = conn;
110         }
111 }
112
113 void backend_new_plan(struct io_conn *conn)
114 {
115         struct pollfd *pfd = &pollfds[conn->fd.backend_info];
116
117         if (pfd->events)
118                 num_waiting--;
119
120         pfd->events = 0;
121         if (conn->plan[IO_IN].status == IO_POLLING)
122                 pfd->events |= POLLIN;
123         if (conn->plan[IO_OUT].status == IO_POLLING)
124                 pfd->events |= POLLOUT;
125
126         if (pfd->events) {
127                 num_waiting++;
128                 pfd->fd = conn->fd.fd;
129         } else {
130                 pfd->fd = -conn->fd.fd;
131         }
132 }
133
134 void backend_wake(const void *wait)
135 {
136         unsigned int i;
137
138         for (i = 0; i < num_fds; i++) {
139                 struct io_conn *c;
140
141                 /* Ignore listeners */
142                 if (fds[i]->listener)
143                         continue;
144
145                 c = (void *)fds[i];
146                 if (c->plan[IO_IN].status == IO_WAITING
147                     && c->plan[IO_IN].u1.const_vp == wait)
148                         io_do_wakeup(c, &c->plan[IO_IN]);
149
150                 if (c->plan[IO_OUT].status == IO_WAITING
151                     && c->plan[IO_OUT].u1.const_vp == wait)
152                         io_do_wakeup(c, &c->plan[IO_OUT]);
153         }
154 }
155
156 bool add_conn(struct io_conn *c)
157 {
158         return add_fd(&c->fd, 0);
159 }
160
161 static void del_conn(struct io_conn *conn)
162 {
163         del_fd(&conn->fd);
164         if (conn->finish) {
165                 /* Saved by io_close */
166                 errno = conn->plan[IO_IN].u1.s;
167                 conn->finish(conn, conn->finish_arg);
168         }
169         tal_free(conn);
170 }
171
172 void del_listener(struct io_listener *l)
173 {
174         del_fd(&l->fd);
175 }
176
177 static void accept_conn(struct io_listener *l)
178 {
179         int fd = accept(l->fd.fd, NULL, NULL);
180
181         /* FIXME: What to do here? */
182         if (fd < 0)
183                 return;
184
185         io_new_conn(l->ctx, fd, l->init, l->arg);
186 }
187
188 /* It's OK to miss some, as long as we make progress. */
189 static bool close_conns(void)
190 {
191         bool ret = false;
192
193         while (closing) {
194                 struct io_conn *conn = closing;
195
196                 assert(conn->plan[IO_IN].status == IO_CLOSING);
197                 assert(conn->plan[IO_OUT].status == IO_CLOSING);
198
199                 closing = closing->list;
200                 del_conn(conn);
201                 ret = true;
202         }
203         return ret;
204 }
205
206 static bool handle_always(void)
207 {
208         bool ret = false;
209
210         while (always) {
211                 struct io_conn *conn = always;
212
213                 assert(conn->plan[IO_IN].status == IO_ALWAYS
214                        || conn->plan[IO_OUT].status == IO_ALWAYS);
215
216                 /* Remove from list, and mark it so it knows that. */
217                 always = always->list;
218                 conn->list = NULL;
219                 io_do_always(conn);
220                 ret = true;
221         }
222         return ret;
223 }
224
225 /* This is the main loop. */
226 void *io_loop(void)
227 {
228         void *ret;
229
230         while (!io_loop_return) {
231                 int i, r;
232
233                 if (close_conns()) {
234                         /* Could have started/finished more. */
235                         continue;
236                 }
237
238                 if (handle_always()) {
239                         /* Could have started/finished more. */
240                         continue;
241                 }
242
243                 /* Everything closed? */
244                 if (num_fds == 0)
245                         break;
246
247                 /* You can't tell them all to go to sleep! */
248                 assert(num_waiting);
249
250                 r = poll(pollfds, num_fds, -1);
251                 if (r < 0)
252                         break;
253
254                 for (i = 0; i < num_fds && !io_loop_return; i++) {
255                         struct io_conn *c = (void *)fds[i];
256                         int events = pollfds[i].revents;
257
258                         if (r == 0)
259                                 break;
260
261                         if (fds[i]->listener) {
262                                 if (events & POLLIN) {
263                                         accept_conn((void *)c);
264                                         r--;
265                                 }
266                         } else if (events & (POLLIN|POLLOUT)) {
267                                 r--;
268                                 io_ready(c, events);
269                         } else if (events & (POLLHUP|POLLNVAL|POLLERR)) {
270                                 r--;
271                                 errno = EBADF;
272                                 io_close(c);
273                         }
274                 }
275         }
276
277         close_conns();
278
279         ret = io_loop_return;
280         io_loop_return = NULL;
281
282         return ret;
283 }