]> git.ozlabs.org Git - ccan/blob - ccan/io/poll.c
070f6d842ae7f45bd4b0b529b6c31cd6c71d7b20
[ccan] / ccan / io / poll.c
1 /* Licensed under BSD-MIT - 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
10 static size_t num_fds = 0, max_fds = 0, num_next = 0, num_finished = 0;
11 static struct pollfd *pollfds = NULL;
12 static struct fd **fds = NULL;
13
14 static bool add_fd(struct fd *fd, short events)
15 {
16         if (num_fds + 1 > max_fds) {
17                 struct pollfd *newpollfds;
18                 struct fd **newfds;
19                 size_t num = max_fds ? max_fds * 2 : 8;
20
21                 newpollfds = realloc(pollfds, sizeof(*newpollfds) * num);
22                 if (!newpollfds)
23                         return false;
24                 pollfds = newpollfds;
25                 newfds = realloc(fds, sizeof(*newfds) * num);
26                 if (!newfds)
27                         return false;
28                 fds = newfds;
29                 max_fds = num;
30         }
31
32         pollfds[num_fds].fd = fd->fd;
33         pollfds[num_fds].events = events;
34         pollfds[num_fds].revents = 0; /* In case we're iterating now */
35         fds[num_fds] = fd;
36         fd->backend_info = num_fds;
37         num_fds++;
38         return true;
39 }
40
41 static void del_fd(struct fd *fd)
42 {
43         size_t n = fd->backend_info;
44
45         assert(n != -1);
46         assert(n < num_fds);
47         if (n != num_fds - 1) {
48                 /* Move last one over us. */
49                 pollfds[n] = pollfds[num_fds-1];
50                 fds[n] = fds[num_fds-1];
51                 assert(fds[n]->backend_info == num_fds-1);
52                 fds[n]->backend_info = n;
53         } else if (num_fds == 1) {
54                 /* Free everything when no more fds. */
55                 free(pollfds);
56                 free(fds);
57                 pollfds = NULL;
58                 fds = NULL;
59                 max_fds = 0;
60         }
61         num_fds--;
62         fd->backend_info = -1;
63         close(fd->fd);
64 }
65
66 bool add_listener(struct io_listener *l)
67 {
68         return add_fd(&l->fd, POLLIN);
69 }
70
71 bool add_conn(struct io_conn *c)
72 {
73         if (!add_fd(&c->fd, 0))
74                 return false;
75         num_next++;
76         return true;
77 }
78
79 static void del_conn(struct io_conn *conn)
80 {
81         if (conn->fd.finish)
82                 conn->fd.finish(conn, conn->fd.finish_arg);
83         del_fd(&conn->fd);
84         if (conn->state == FINISHED)
85                 num_finished--;
86         else if (conn->state == NEXT)
87                 num_next--;
88 }
89
90 void del_listener(struct io_listener *l)
91 {
92         del_fd(&l->fd);
93 }
94
95 void backend_set_state(struct io_conn *conn, struct io_op *op)
96 {
97         enum io_state state = from_ioop(op);
98         struct pollfd *pfd = &pollfds[conn->fd.backend_info];
99
100         switch (state) {
101         case READ:
102         case READPART:
103                 pfd->events = POLLIN;
104                 break;
105         case WRITE:
106         case WRITEPART:
107                 pfd->events = POLLOUT;
108                 break;
109         case IDLE:
110                 pfd->events = 0;
111                 break;
112         case NEXT:
113                 num_next++;
114                 break;
115         case FINISHED:
116                 num_finished++;
117                 break;
118         default:
119                 abort();
120         }
121         conn->state = state;
122 }
123
124 static void accept_conn(struct io_listener *l)
125 {
126         struct io_conn *c;
127         int fd = accept(l->fd.fd, NULL, NULL);
128
129         /* FIXME: What to do here? */
130         if (fd < 0)
131                 return;
132         c = io_new_conn(fd, l->fd.next, l->fd.finish, l->fd.next_arg);
133         if (!c) {
134                 close(fd);
135                 return;
136         }
137 }
138
139 /* It's OK to miss some, as long as we make progress. */
140 static void finish_and_next(bool finished_only)
141 {
142         unsigned int i;
143
144         for (i = 0; !io_loop_return && i < num_fds; i++) {
145                 struct io_conn *c;
146
147                 if (!num_finished) {
148                         if (finished_only || num_next == 0)
149                                 break;
150                 }
151                 if (fds[i]->listener)
152                         continue;
153                 c = (void *)fds[i];
154                 if (c->state == FINISHED) {
155                         del_conn(c);
156                         free(c);
157                         i--;
158                 } else if (!finished_only && c->state == NEXT) {
159                         backend_set_state(c, c->fd.next(c, c->fd.next_arg));
160                         num_next--;
161                 }
162         }
163 }
164
165 /* This is the main loop. */
166 void *io_loop(void)
167 {
168         void *ret;
169
170         while (!io_loop_return) {
171                 int i, r;
172
173                 if (num_finished || num_next) {
174                         finish_and_next(false);
175                         /* Could have started/finished more. */
176                         continue;
177                 }
178
179                 if (num_fds == 0)
180                         break;
181
182                 r = poll(pollfds, num_fds, -1);
183                 if (r < 0)
184                         break;
185
186                 for (i = 0; i < num_fds && !io_loop_return; i++) {
187                         struct io_conn *c = (void *)fds[i];
188                         if (pollfds[i].revents & POLLOUT)
189                                 backend_set_state(c, do_writeable(c));
190                         else if (pollfds[i].revents & POLLIN) {
191                                 if (fds[i]->listener)
192                                         accept_conn((void *)c);
193                                 else
194                                         backend_set_state(c, do_readable(c));
195                         } else if (pollfds[i].revents & POLLHUP) {
196                                 backend_set_state(c, io_close(c, NULL));
197                         }
198                 }
199         }
200
201         while (num_finished)
202                 finish_and_next(true);
203
204         ret = io_loop_return;
205         io_loop_return = NULL;
206         return ret;
207 }