1 /* Licensed under LGPLv2.1+ - see LICENSE file for details */
8 #include <sys/socket.h>
11 #include <ccan/list/list.h>
12 #include <ccan/time/time.h>
13 #include <ccan/timer/timer.h>
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;
20 static bool add_fd(struct fd *fd, short events)
24 pollfds = tal_arr(NULL, struct pollfd, 8);
27 fds = tal_arr(pollfds, struct fd *, 8);
33 if (num_fds + 1 > max_fds) {
34 size_t num = max_fds * 2;
36 if (!tal_resize(&pollfds, num))
38 if (!tal_resize(&fds, num))
43 pollfds[num_fds].events = events;
44 /* In case it's idle. */
46 pollfds[num_fds].fd = -fd->fd;
48 pollfds[num_fds].fd = fd->fd;
49 pollfds[num_fds].revents = 0; /* In case we're iterating now */
51 fd->backend_info = num_fds;
59 static void del_fd(struct fd *fd)
61 size_t n = fd->backend_info;
65 if (pollfds[n].events)
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);
80 fd->backend_info = -1;
84 bool add_listener(struct io_listener *l)
86 if (!add_fd(&l->fd, POLLIN))
91 void backend_new_closing(struct io_conn *conn)
93 /* Already on always list? Remove it. */
95 struct io_conn **p = &always;
103 conn->list = closing;
107 void backend_new_always(struct io_conn *conn)
109 /* May already be in always list (other plan), or closing. */
116 void backend_new_plan(struct io_conn *conn)
118 struct pollfd *pfd = &pollfds[conn->fd.backend_info];
124 if (conn->plan[IO_IN].status == IO_POLLING)
125 pfd->events |= POLLIN;
126 if (conn->plan[IO_OUT].status == IO_POLLING)
127 pfd->events |= POLLOUT;
131 pfd->fd = conn->fd.fd;
133 pfd->fd = -conn->fd.fd;
137 void backend_wake(const void *wait)
141 for (i = 0; i < num_fds; i++) {
144 /* Ignore listeners */
145 if (fds[i]->listener)
149 if (c->plan[IO_IN].status == IO_WAITING
150 && c->plan[IO_IN].u1.const_vp == wait)
151 io_do_wakeup(c, &c->plan[IO_IN]);
153 if (c->plan[IO_OUT].status == IO_WAITING
154 && c->plan[IO_OUT].u1.const_vp == wait)
155 io_do_wakeup(c, &c->plan[IO_OUT]);
159 bool add_conn(struct io_conn *c)
161 return add_fd(&c->fd, 0);
164 static void del_conn(struct io_conn *conn)
168 /* Saved by io_close */
169 errno = conn->plan[IO_IN].u1.s;
170 conn->finish(conn, conn->finish_arg);
175 void del_listener(struct io_listener *l)
180 static void accept_conn(struct io_listener *l)
182 int fd = accept(l->fd.fd, NULL, NULL);
184 /* FIXME: What to do here? */
188 io_new_conn(l->ctx, fd, l->init, l->arg);
191 /* It's OK to miss some, as long as we make progress. */
192 static bool close_conns(void)
197 struct io_conn *conn = closing;
199 assert(conn->plan[IO_IN].status == IO_CLOSING);
200 assert(conn->plan[IO_OUT].status == IO_CLOSING);
202 closing = closing->list;
209 static bool handle_always(void)
214 struct io_conn *conn = always;
216 assert(conn->plan[IO_IN].status == IO_ALWAYS
217 || conn->plan[IO_OUT].status == IO_ALWAYS);
219 /* Remove from list, and mark it so it knows that. */
220 always = always->list;
228 /* This is the main loop. */
229 void *io_loop(struct timers *timers, struct list_head *expired)
233 /* if timers is NULL, expired must be. If not, not. */
234 assert(!timers == !expired);
236 /* Make sure this is empty if we exit for some other reason. */
238 list_head_init(expired);
240 while (!io_loop_return) {
241 int i, r, ms_timeout = -1;
244 /* Could have started/finished more. */
248 if (handle_always()) {
249 /* Could have started/finished more. */
253 /* Everything closed? */
257 /* You can't tell them all to go to sleep! */
261 struct timeabs now, first;
265 /* Call functions for expired timers. */
266 timers_expire(timers, now, expired);
267 if (!list_empty(expired))
270 /* Now figure out how long to wait for the next one. */
271 if (timer_earliest(timers, &first)) {
273 next = time_to_msec(time_between(first, now));
277 ms_timeout = INT_MAX;
281 r = poll(pollfds, num_fds, ms_timeout);
285 for (i = 0; i < num_fds && !io_loop_return; i++) {
286 struct io_conn *c = (void *)fds[i];
287 int events = pollfds[i].revents;
292 if (fds[i]->listener) {
293 if (events & POLLIN) {
294 accept_conn((void *)c);
297 } else if (events & (POLLIN|POLLOUT)) {
300 } else if (events & (POLLHUP|POLLNVAL|POLLERR)) {
310 ret = io_loop_return;
311 io_loop_return = NULL;