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