X-Git-Url: http://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Fio%2Fio.h;h=bcdb11fdd0e0c1f1e9a08aac55a13e0afe888d92;hp=8199bda9802050f747440f66ec1da97f987fcb47;hb=12ab811533406e22b78ce8b227474f1056375ae4;hpb=3a7b8a8a8081ebbb6457527de376dec6264bc381 diff --git a/ccan/io/io.h b/ccan/io/io.h index 8199bda9..bcdb11fd 100644 --- a/ccan/io/io.h +++ b/ccan/io/io.h @@ -5,76 +5,30 @@ #include #include #include - -struct io_conn; - -/** - * struct io_plan - returned from a setup function. - * - * A plan of what IO to do, when. - */ -struct io_plan { - int pollflag; - /* Only NULL if idle. */ - int (*io)(int fd, struct io_plan *plan); - /* Only NULL if closing. */ - struct io_plan (*next)(struct io_conn *, void *arg); - void *next_arg; - - union { - struct { - char *buf; - size_t len; - } read; - struct { - const char *buf; - size_t len; - } write; - struct { - char *buf; - size_t *lenp; - } readpart; - struct { - const char *buf; - size_t *lenp; - } writepart; - struct { - void *p; - size_t len; - } ptr_len; - struct { - void *p1; - void *p2; - } ptr_ptr; - struct { - size_t len1; - size_t len2; - } len_len; - } u; -}; - -#ifdef DEBUG -extern bool io_plan_for_other; -extern bool (*io_debug)(struct io_conn *conn); -#define io_plan_other() ((io_plan_for_other = true)) -void io_plan_debug(struct io_plan *plan); -#else -#define io_plan_other() (void)0 -static inline void io_plan_debug(struct io_plan *plan) { } -#endif +#include "io_plan.h" /** * io_new_conn - create a new connection. * @fd: the file descriptor. - * @plan: the first I/O function. + * @plan: the first I/O to perform. * * This creates a connection which owns @fd. @plan will be called on the * next io_loop(). * * Returns NULL on error (and sets errno). + * + * Example: + * int fd[2]; + * struct io_conn *conn; + * + * pipe(fd); + * // Plan is to close the fd immediately. + * conn = io_new_conn(fd[0], io_close()); + * if (!conn) + * exit(1); */ #define io_new_conn(fd, plan) \ - (io_plan_other(), io_new_conn_((fd), (plan))) + (io_plan_no_debug(), io_new_conn_((fd), (plan))) struct io_conn *io_new_conn_(int fd, struct io_plan plan); /** @@ -84,7 +38,17 @@ struct io_conn *io_new_conn_(int fd, struct io_plan plan); * @arg: the argument to @finish. * * @finish will be called when an I/O operation fails, or you call - * io_close() on the connection. + * io_close() on the connection. errno will be set to the value + * after the failed I/O, or at the call to io_close(). + * + * Example: + * static void finish(struct io_conn *conn, void *unused) + * { + * // errno is not 0 after success, so this is a bit useless. + * printf("Conn %p closed with errno %i\n", conn, errno); + * } + * ... + * io_set_finish(conn, finish, NULL); */ #define io_set_finish(conn, finish, arg) \ io_set_finish_((conn), \ @@ -105,6 +69,50 @@ void io_set_finish_(struct io_conn *conn, * When @fd becomes readable, we accept() and pass that fd to init(). * * Returns NULL on error (and sets errno). + * + * Example: + * #include + * #include + * #include + * + * static void start_conn(int fd, char *msg) + * { + * printf("%s fd %i\n", msg, fd); + * close(fd); + * } + * + * // Set up a listening socket, return it. + * static struct io_listener *do_listen(const char *port) + * { + * struct addrinfo *addrinfo, hints; + * int fd, on = 1; + * + * memset(&hints, 0, sizeof(hints)); + * hints.ai_family = AF_UNSPEC; + * hints.ai_socktype = SOCK_STREAM; + * hints.ai_flags = AI_PASSIVE; + * hints.ai_protocol = 0; + * + * if (getaddrinfo(NULL, port, &hints, &addrinfo) != 0) + * return NULL; + * + * fd = socket(addrinfo->ai_family, addrinfo->ai_socktype, + * addrinfo->ai_protocol); + * if (fd < 0) + * return NULL; + * + * freeaddrinfo(addrinfo); + * setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + * if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) { + * close(fd); + * return NULL; + * } + * if (listen(fd, 1) != 0) { + * close(fd); + * return NULL; + * } + * return io_new_listener(fd, start_conn, (char *)"Got one!"); + * } */ #define io_new_listener(fd, init, arg) \ io_new_listener_((fd), \ @@ -121,113 +129,255 @@ struct io_listener *io_new_listener_(int fd, * @listener: the listener returned from io_new_listener. * * This closes the fd and frees @listener. + * + * Example: + * ... + * struct io_listener *l = do_listen("8111"); + * if (l) { + * io_loop(); + * io_close_listener(l); + * } */ void io_close_listener(struct io_listener *listener); /** - * io_write - queue data to be written. + * io_write - plan to write data. * @data: the data buffer. * @len: the length to write. * @cb: function to call once it's done. * @arg: @cb argument * - * This will queue the data buffer for writing. Once it's all + * This creates a plan write out a data buffer. Once it's all * written, the @cb function will be called: on an error, the finish * function is called instead. * * Note that the I/O may actually be done immediately. + * + * Example: + * static void start_conn_with_write(int fd, const char *msg) + * { + * // Write message, then close. + * io_new_conn(fd, io_write(msg, strlen(msg), io_close_cb, NULL)); + * } */ #define io_write(data, len, cb, arg) \ - io_write_((data), (len), \ - typesafe_cb_preargs(struct io_plan, void *, \ - (cb), (arg), struct io_conn *), \ - (arg)) + io_debug(io_write_((data), (len), \ + typesafe_cb_preargs(struct io_plan, void *, \ + (cb), (arg), struct io_conn *), \ + (arg))) struct io_plan io_write_(const void *data, size_t len, struct io_plan (*cb)(struct io_conn *, void *), void *arg); /** - * io_read - queue buffer to be read. + * io_read - plan to read data. * @data: the data buffer. * @len: the length to read. * @cb: function to call once it's done. * @arg: @cb argument * - * This will queue the data buffer for reading. Once it's all read, - * the @cb function will be called: on an error, the finish function - * is called instead. + * This creates a plan to read data into a buffer. Once it's all + * read, the @cb function will be called: on an error, the finish + * function is called instead. * * Note that the I/O may actually be done immediately. + * + * Example: + * static void start_conn_with_read(int fd, char msg[12]) + * { + * // Read message, then close. + * io_new_conn(fd, io_read(msg, 12, io_close_cb, NULL)); + * } */ #define io_read(data, len, cb, arg) \ - io_read_((data), (len), \ - typesafe_cb_preargs(struct io_plan, void *, \ - (cb), (arg), struct io_conn *), \ - (arg)) + io_debug(io_read_((data), (len), \ + typesafe_cb_preargs(struct io_plan, void *, \ + (cb), (arg), struct io_conn *), \ + (arg))) struct io_plan io_read_(void *data, size_t len, struct io_plan (*cb)(struct io_conn *, void *), void *arg); /** - * io_read_partial - queue buffer to be read (partial OK). + * io_read_partial - plan to read some data. * @data: the data buffer. * @len: the maximum length to read, set to the length actually read. * @cb: function to call once it's done. * @arg: @cb argument * - * This will queue the data buffer for reading. Once any data is + * This creates a plan to read data into a buffer. Once any data is * read, @len is updated and the @cb function will be called: on an * error, the finish function is called instead. * * Note that the I/O may actually be done immediately. + * + * Example: + * struct buf { + * size_t len; + * char buf[12]; + * }; + * + * static struct io_plan dump_and_close(struct io_conn *conn, struct buf *b) + * { + * printf("Partial read: '%*s'\n", (int)b->len, b->buf); + * free(b); + * return io_close(); + * } + * + * static void start_conn_with_part_read(int fd, void *unused) + * { + * struct buf *b = malloc(sizeof(*b)); + * + * // Read message, then dump and close. + * b->len = sizeof(b->buf); + * io_new_conn(fd, io_read_partial(b->buf, &b->len, dump_and_close, b)); + * } */ #define io_read_partial(data, len, cb, arg) \ - io_read_partial_((data), (len), \ - typesafe_cb_preargs(struct io_plan, void *, \ - (cb), (arg), struct io_conn *), \ - (arg)) + io_debug(io_read_partial_((data), (len), \ + typesafe_cb_preargs(struct io_plan, void *, \ + (cb), (arg), \ + struct io_conn *), \ + (arg))) struct io_plan io_read_partial_(void *data, size_t *len, struct io_plan (*cb)(struct io_conn *, void *), void *arg); /** - * io_write_partial - queue data to be written (partial OK). + * io_write_partial - plan to write some data. * @data: the data buffer. * @len: the maximum length to write, set to the length actually written. * @cb: function to call once it's done. * @arg: @cb argument * - * This will queue the data buffer for writing. Once any data is + * This creates a plan to write data from a buffer. Once any data is * written, @len is updated and the @cb function will be called: on an * error, the finish function is called instead. * * Note that the I/O may actually be done immediately. + * + * Example: + * struct buf { + * size_t len; + * char buf[12]; + * }; + * + * static struct io_plan show_remainder(struct io_conn *conn, struct buf *b) + * { + * printf("Only wrote: '%*s'\n", (int)b->len, b->buf); + * free(b); + * return io_close(); + * } + * + * static void start_conn_with_part_read(int fd, void *unused) + * { + * struct buf *b = malloc(sizeof(*b)); + * + * // Write message, then dump and close. + * b->len = sizeof(b->buf); + * strcpy(b->buf, "Hello world"); + * io_new_conn(fd, io_write_partial(b->buf, &b->len, show_remainder, b)); + * } */ #define io_write_partial(data, len, cb, arg) \ - io_write_partial_((data), (len), \ - typesafe_cb_preargs(struct io_plan, void *, \ - (cb), (arg), struct io_conn *), \ - (arg)) + io_debug(io_write_partial_((data), (len), \ + typesafe_cb_preargs(struct io_plan, void *, \ + (cb), (arg), \ + struct io_conn *), \ + (arg))) struct io_plan io_write_partial_(const void *data, size_t *len, struct io_plan (*cb)(struct io_conn *, void*), void *arg); +/** + * io_always - plan to immediately call next callback. + * @cb: function to call. + * @arg: @cb argument + * + * Sometimes it's neater to plan a callback rather than call it directly; + * for example, if you only need to read data for one path and not another. + * + * Example: + * static void start_conn_with_nothing(int fd) + * { + * // Silly example: close on next time around loop. + * io_new_conn(fd, io_always(io_close_cb, NULL)); + * } + */ +#define io_always(cb, arg) \ + io_debug(io_always_(typesafe_cb_preargs(struct io_plan, void *, \ + (cb), (arg), \ + struct io_conn *), \ + (arg))) +struct io_plan io_always_(struct io_plan (*cb)(struct io_conn *, void *), + void *arg); /** - * io_idle - explicitly note that this connection will do nothing. + * io_connect - plan to connect to a listening socket. + * @fd: file descriptor. + * @addr: where to connect. + * @cb: function to call once it's done. + * @arg: @cb argument + * + * This initiates a connection, and creates a plan for + * (asynchronously). completing it. Once complete, @len is updated + * and the @cb function will be called: on an error, the finish + * function is called instead. * - * This indicates the connection is idle: some other function will - * later call io_read/io_write etc. (or io_close) on it, in which case - * it will do that. + * Note that the connect may actually be done immediately. + * + * Example: + * #include + * #include + * #include + * + * // Write, then close socket. + * static struct io_plan start_write(struct io_conn *conn, void *unused) + * { + * return io_write("hello", 5, io_close_cb, NULL); + * } + * + * ... + * + * int fd; + * struct addrinfo *addrinfo; + * + * fd = socket(AF_INET, SOCK_STREAM, 0); + * getaddrinfo("localhost", "8111", NULL, &addrinfo); + * io_new_conn(fd, io_connect(fd, addrinfo, start_write, NULL)); */ -struct io_plan io_idle(void); +struct addrinfo; +#define io_connect(fd, addr, cb, arg) \ + io_debug(io_connect_((fd), (addr), \ + typesafe_cb_preargs(struct io_plan, void *, \ + (cb), (arg), \ + struct io_conn *), \ + (arg))) +struct io_plan io_connect_(int fd, const struct addrinfo *addr, + struct io_plan (*cb)(struct io_conn *, void*), + void *arg); /** - * io_timeout - set timeout function if the callback doesn't fire. + * io_idle - plan to do nothing. + * + * This indicates the connection is idle: io_wake() will be called later do + * give the connection a new plan. + * + * Example: + * struct io_conn *sleeper; + * sleeper = io_new_conn(open("/dev/null", O_RDONLY), io_idle()); + * if (!sleeper) + * exit(1); + */ +#define io_idle() io_debug(io_idle_()) +struct io_plan io_idle_(void); + +/** + * io_timeout - set timeout function if the callback doesn't complete. * @conn: the current connection. * @ts: how long until the timeout should be called. - * @cb to call. + * @cb: callback to call. * @arg: argument to @cb. * * If the usual next callback is not called for this connection before @ts, @@ -236,6 +386,17 @@ struct io_plan io_idle(void); * * Returns false on allocation failure. A connection can only have one * timeout. + * + * Example: + * static struct io_plan close_on_timeout(struct io_conn *conn, char *msg) + * { + * printf("%s\n", msg); + * return io_close(); + * } + * + * ... + * io_timeout(sleeper, time_from_msec(100), + * close_on_timeout, (char *)"Bye!"); */ #define io_timeout(conn, ts, fn, arg) \ io_timeout_((conn), (ts), \ @@ -252,27 +413,62 @@ bool io_timeout_(struct io_conn *conn, struct timespec ts, * @plan: the first I/O function to call. * * Sometimes you want to be able to simultaneously read and write on a - * single fd, but io forces a linear call sequence. The solition is + * single fd, but io forces a linear call sequence. The solution is * to have two connections for the same fd, and use one for read * operations and one for write. * * You must io_close() both of them to close the fd. + * + * Example: + * static void setup_read_write(int fd, + * char greet_in[5], const char greet_out[5]) + * { + * struct io_conn *writer, *reader; + * + * // Read their greeting and send ours at the same time. + * writer = io_new_conn(fd, + * io_write(greet_out, 5, io_close_cb, NULL)); + * reader = io_duplex(writer, + * io_read(greet_in, 5, io_close_cb, NULL)); + * if (!reader || !writer) + * exit(1); + * } */ #define io_duplex(conn, plan) \ - (io_plan_other(), io_duplex_((conn), (plan))) - + (io_plan_no_debug(), io_duplex_((conn), (plan))) struct io_conn *io_duplex_(struct io_conn *conn, struct io_plan plan); /** * io_wake - wake up an idle connection. * @conn: an idle connection. - * @plan: the next I/O function for @conn. + * @plan: the next I/O plan for @conn. + * + * This makes @conn ready to do I/O the next time around the io_loop(). * - * This makes @conn do I/O the next time around the io_loop(). + * Example: + * struct io_conn *sleeper; + * sleeper = io_new_conn(open("/dev/null", O_RDONLY), io_idle()); + * + * io_wake(sleeper, io_write("junk", 4, io_close_cb, NULL)); */ -#define io_wake(conn, plan) (io_plan_other(), io_wake_((conn), (plan))) +#define io_wake(conn, plan) (io_plan_no_debug(), io_wake_((conn), (plan))) void io_wake_(struct io_conn *conn, struct io_plan plan); +/** + * io_is_idle - is a connection idle? + * + * This can be useful for complex protocols, eg. where you want a connection + * to send something, so you queue it and wake it if it's idle. + * + * Example: + * struct io_conn *sleeper; + * sleeper = io_new_conn(open("/dev/null", O_RDONLY), io_idle()); + * + * assert(io_is_idle(sleeper)); + * io_wake(sleeper, io_write("junk", 4, io_close_cb, NULL)); + */ +bool io_is_idle(const struct io_conn *conn); + /** * io_break - return from io_loop() * @ret: non-NULL value to return from io_loop(). @@ -283,8 +479,14 @@ void io_wake_(struct io_conn *conn, struct io_plan plan); * finish callbacks called, then io_loop() with return with @ret. * * If io_loop() is called again, then @plan will be carried out. + * + * Example: + * static struct io_plan fail_on_timeout(struct io_conn *conn, char *msg) + * { + * return io_break(msg, io_close()); + * } */ -#define io_break(ret, plan) (io_plan_other(), io_break_((ret), (plan))) +#define io_break(ret, plan) (io_plan_no_debug(), io_break_((ret), (plan))) struct io_plan io_break_(void *ret, struct io_plan plan); /* FIXME: io_recvfrom/io_sendto */ @@ -293,8 +495,16 @@ struct io_plan io_break_(void *ret, struct io_plan plan); * io_close - plan to close a connection. * * On return to io_loop, the connection will be closed. + * + * Example: + * static struct io_plan close_on_timeout(struct io_conn *conn, const char *msg) + * { + * printf("closing: %s\n", msg); + * return io_close(); + * } */ -struct io_plan io_close(void); +#define io_close() io_debug(io_close_()) +struct io_plan io_close_(void); /** * io_close_cb - helper callback to close a connection. @@ -302,14 +512,59 @@ struct io_plan io_close(void); * * This schedules a connection to be closed; designed to be used as * a callback function. + * + * Example: + * #define close_on_timeout io_close_cb */ struct io_plan io_close_cb(struct io_conn *, void *unused); +/** + * io_close_other - close different connection next time around the I/O loop. + * @conn: the connection to close. + * + * This is used to force a different connection to close: no more I/O will + * happen on @conn, even if it's pending. + * + * It's a bug to use this on the current connection! + * + * Example: + * static void stop_connection(struct io_conn *conn) + * { + * printf("forcing stop on connection\n"); + * io_close_other(conn); + * } + */ +void io_close_other(struct io_conn *conn); + /** * io_loop - process fds until all closed on io_break. * * This is the core loop; it exits with the io_break() arg, or NULL if * all connections and listeners are closed. + * + * Example: + * io_loop(); */ void *io_loop(void); + +/** + * io_conn_fd - get the fd from a connection. + * @conn: the connection. + * + * Sometimes useful, eg for getsockname(). + */ +int io_conn_fd(const struct io_conn *conn); + +/** + * io_set_alloc - set alloc/realloc/free function for io to use. + * @allocfn: allocator function + * @reallocfn: reallocator function, ptr may be NULL, size never 0. + * @freefn: free function + * + * By default io uses malloc/realloc/free, and returns NULL if they fail. + * You can set your own variants here. + */ +void io_set_alloc(void *(*allocfn)(size_t size), + void *(*reallocfn)(void *ptr, size_t size), + void (*freefn)(void *ptr)); #endif /* CCAN_IO_H */