]> git.ozlabs.org Git - ccan/blob - ccan/io/io.c
325db7872153952895b60d5b6dc4c99f1cb9fe0a
[ccan] / ccan / io / io.c
1 /* Licensed under BSD-MIT - see LICENSE file for details */
2 #include "io.h"
3 #include "backend.h"
4 #include <sys/types.h>
5 #include <sys/socket.h>
6 #include <netdb.h>
7 #include <string.h>
8 #include <errno.h>
9 #include <stdlib.h>
10 #include <assert.h>
11
12 void *io_loop_return;
13
14 struct io_listener *io_new_listener_(int fd,
15                                      struct io_op *(*start)(struct io_conn *,
16                                                             void *arg),
17                                      void (*finish)(struct io_conn *, void *),
18                                      void *arg)
19 {
20         struct io_listener *l = malloc(sizeof(*l));
21
22         if (!l)
23                 return NULL;
24
25         l->fd.listener = true;
26         l->fd.fd = fd;
27         l->fd.next = start;
28         l->fd.finish = finish;
29         l->fd.finish_arg = l->fd.next_arg = arg;
30         if (!add_listener(l)) {
31                 free(l);
32                 return NULL;
33         }
34         return l;
35 }
36
37 void io_close_listener(struct io_listener *l)
38 {
39         close(l->fd.fd);
40         del_listener(l);
41         free(l);
42 }
43
44 struct io_conn *io_new_conn_(int fd,
45                              struct io_op *(*start)(struct io_conn *, void *),
46                              void (*finish)(struct io_conn *, void *),
47                              void *arg)
48 {
49         struct io_conn *conn = malloc(sizeof(*conn));
50
51         if (!conn)
52                 return NULL;
53
54         conn->fd.listener = false;
55         conn->fd.fd = fd;
56         conn->fd.next = start;
57         conn->fd.finish = finish;
58         conn->fd.finish_arg = conn->fd.next_arg = arg;
59         conn->state = NEXT;
60         if (!add_conn(conn)) {
61                 free(conn);
62                 return NULL;
63         }
64         return conn;
65 }
66
67 /* Convenient token which only we can produce. */
68 static inline struct io_next *to_ionext(struct io_conn *conn)
69 {
70         return (struct io_next *)conn;
71 }
72
73 static inline struct io_op *to_ioop(enum io_state state)
74 {
75         return (struct io_op *)(long)state;
76 }
77
78 static inline struct io_conn *from_ionext(struct io_next *next)
79 {
80         return (struct io_conn *)next;
81 }
82
83 struct io_next *io_next_(struct io_conn *conn,
84                          struct io_op *(*next)(struct io_conn *, void *),
85                          void *arg)
86 {
87         conn->fd.next = next;
88         conn->fd.next_arg = arg;
89
90         return to_ionext(conn);
91 }
92
93 /* Queue some data to be written. */
94 struct io_op *io_write(const void *data, size_t len, struct io_next *next)
95 {
96         struct io_conn *conn = from_ionext(next);
97         conn->u.write.buf = data;
98         conn->u.write.len = len;
99         return to_ioop(WRITE);
100 }
101
102 /* Queue a request to read into a buffer. */
103 struct io_op *io_read(void *data, size_t len, struct io_next *next)
104 {
105         struct io_conn *conn = from_ionext(next);
106         conn->u.read.buf = data;
107         conn->u.read.len = len;
108         return to_ioop(READ);
109 }
110
111 /* Queue a partial request to read into a buffer. */
112 struct io_op *io_read_partial(void *data, size_t *len, struct io_next *next)
113 {
114         struct io_conn *conn = from_ionext(next);
115         conn->u.readpart.buf = data;
116         conn->u.readpart.lenp = len;
117         return to_ioop(READPART);
118 }
119
120 /* Queue a partial write request. */
121 struct io_op *io_write_partial(const void *data, size_t *len, struct io_next *next)
122 {
123         struct io_conn *conn = from_ionext(next);
124         conn->u.writepart.buf = data;
125         conn->u.writepart.lenp = len;
126         return to_ioop(WRITEPART);
127 }
128
129 struct io_op *io_idle(struct io_conn *conn)
130 {
131         return to_ioop(IDLE);
132 }
133
134 void io_wake_(struct io_conn *conn,
135               struct io_op *(*next)(struct io_conn *, void *), void *arg)
136
137 {
138         /* It might have finished, but we haven't called its finish() yet. */
139         if (conn->state == FINISHED)
140                 return;
141         assert(conn->state == IDLE);
142         conn->fd.next = next;
143         conn->fd.next_arg = arg;
144         backend_set_state(conn, to_ioop(NEXT));
145 }
146
147 static struct io_op *do_next(struct io_conn *conn)
148 {
149         return conn->fd.next(conn, conn->fd.next_arg);
150 }
151
152 struct io_op *do_writeable(struct io_conn *conn)
153 {
154         ssize_t ret;
155         bool finished;
156
157         switch (conn->state) {
158         case WRITE:
159                 ret = write(conn->fd.fd, conn->u.write.buf, conn->u.write.len);
160                 if (ret < 0)
161                         return io_close(conn, NULL);
162                 conn->u.write.buf += ret;
163                 conn->u.write.len -= ret;
164                 finished = (conn->u.write.len == 0);
165                 break;
166         case WRITEPART:
167                 ret = write(conn->fd.fd, conn->u.writepart.buf,
168                             *conn->u.writepart.lenp);
169                 if (ret < 0)
170                         return io_close(conn, NULL);
171                 *conn->u.writepart.lenp = ret;
172                 finished = true;
173                 break;
174         default:
175                 /* Shouldn't happen. */
176                 abort();
177         }
178
179         if (finished)
180                 return do_next(conn);
181         return to_ioop(conn->state);
182 }
183
184 struct io_op *do_readable(struct io_conn *conn)
185 {
186         ssize_t ret;
187         bool finished;
188
189         switch (conn->state) {
190         case READ:
191                 ret = read(conn->fd.fd, conn->u.read.buf, conn->u.read.len);
192                 if (ret <= 0)
193                         return io_close(conn, NULL);
194                 conn->u.read.buf += ret;
195                 conn->u.read.len -= ret;
196                 finished = (conn->u.read.len == 0);
197                 break;
198         case READPART:
199                 ret = read(conn->fd.fd, conn->u.readpart.buf,
200                             *conn->u.readpart.lenp);
201                 if (ret <= 0)
202                         return io_close(conn, NULL);
203                 *conn->u.readpart.lenp = ret;
204                 finished = true;
205                 break;
206         default:
207                 /* Shouldn't happen. */
208                 abort();
209         }
210
211         if (finished)
212                 return do_next(conn);
213         return to_ioop(conn->state);
214 }
215
216 /* Useful next functions. */
217 /* Close the connection, we're done. */
218 struct io_op *io_close(struct io_conn *conn, void *arg)
219 {
220         return to_ioop(FINISHED);
221 }
222
223 /* Exit the loop, returning this (non-NULL) arg. */
224 struct io_op *io_break(void *arg, struct io_next *next)
225 {
226         io_loop_return = arg;
227
228         return to_ioop(NEXT);
229 }