From: Rusty Russell Date: Mon, 4 Aug 2014 08:17:21 +0000 (+0930) Subject: ccan/io: add io_out_wait() and io_out_always(). X-Git-Url: http://git.ozlabs.org/?p=ccan;a=commitdiff_plain;h=0fbc79090f9ff5bc1caf8c0f0f05525a05f2e82d ccan/io: add io_out_wait() and io_out_always(). This specificity is required, for example, when doing: return io_duplex(conn, io_read(...), io_always(...)); The workaround suggested doesn't work, because io_duplex_prepare() asserts() if the plans aren't UNSET to start. And pettycoin needs this. Signed-off-by: Rusty Russell --- diff --git a/ccan/io/io.c b/ccan/io/io.c index 7f5113d3..c0af6bf7 100644 --- a/ccan/io/io.c +++ b/ccan/io/io.c @@ -125,16 +125,28 @@ static struct io_plan *set_always(struct io_conn *conn, return io_set_plan(conn, dir, NULL, next, arg); } +static struct io_plan *io_always_dir(struct io_conn *conn, + enum io_direction dir, + struct io_plan *(*next)(struct io_conn *, + void *), + void *arg) +{ + return set_always(conn, dir, next, arg); +} + struct io_plan *io_always_(struct io_conn *conn, struct io_plan *(*next)(struct io_conn *, void *), void *arg) { - /* If we're duplex, we want this on the current plan. Otherwise, - * doesn't matter. */ - if (conn->plan[IO_IN].status == IO_UNSET) - return set_always(conn, IO_IN, next, arg); - else - return set_always(conn, IO_OUT, next, arg); + return io_always_dir(conn, IO_IN, next, arg); +} + +struct io_plan *io_out_always_(struct io_conn *conn, + struct io_plan *(*next)(struct io_conn *, + void *), + void *arg) +{ + return io_always_dir(conn, IO_OUT, next, arg); } static int do_write(int fd, struct io_plan_arg *arg) @@ -294,22 +306,14 @@ struct io_plan *io_connect_(struct io_conn *conn, const struct addrinfo *addr, return io_set_plan(conn, IO_IN, do_connect, next, next_arg); } -struct io_plan *io_wait_(struct io_conn *conn, - const void *wait, - struct io_plan *(*next)(struct io_conn *, void *), - void *next_arg) +static struct io_plan *io_wait_dir(struct io_conn *conn, + const void *wait, + enum io_direction dir, + struct io_plan *(*next)(struct io_conn *, + void *), + void *next_arg) { - enum io_direction dir; - struct io_plan_arg *arg; - - /* If we're duplex, we want this on the current plan. Otherwise, - * doesn't matter. */ - if (conn->plan[IO_IN].status == IO_UNSET) - dir = IO_IN; - else - dir = IO_OUT; - - arg = io_plan_arg(conn, dir); + struct io_plan_arg *arg = io_plan_arg(conn, dir); arg->u1.const_vp = wait; conn->plan[dir].status = IO_WAITING; @@ -317,6 +321,22 @@ struct io_plan *io_wait_(struct io_conn *conn, return io_set_plan(conn, dir, NULL, next, next_arg); } +struct io_plan *io_wait_(struct io_conn *conn, + const void *wait, + struct io_plan *(*next)(struct io_conn *, void *), + void *next_arg) +{ + return io_wait_dir(conn, wait, IO_IN, next, next_arg); +} + +struct io_plan *io_out_wait_(struct io_conn *conn, + const void *wait, + struct io_plan *(*next)(struct io_conn *, void *), + void *next_arg) +{ + return io_wait_dir(conn, wait, IO_OUT, next, next_arg); +} + void io_wake(const void *wait) { backend_wake(wait); diff --git a/ccan/io/io.h b/ccan/io/io.h index d0c0a39a..1cf9d609 100644 --- a/ccan/io/io.h +++ b/ccan/io/io.h @@ -362,6 +362,27 @@ struct io_plan *io_always_(struct io_conn *conn, struct io_plan *(*next)(struct io_conn *, void *), void *arg); +/** + * io_out_always - output plan to immediately call next callback + * @conn: the connection that plan is for. + * @next: function to call. + * @arg: @next argument + * + * This is a variant of io_always() which uses the output plan; it only + * matters if you are using io_duplex, and thus have two plans running at + * once. + */ +#define io_out_always(conn, next, arg) \ + io_out_always_((conn), typesafe_cb_preargs(struct io_plan *, void *, \ + (next), (arg), \ + struct io_conn *), \ + (arg)) + +struct io_plan *io_out_always_(struct io_conn *conn, + struct io_plan *(*next)(struct io_conn *, + void *), + void *arg); + /** * io_connect - create an asynchronous connection to a listening socket. * @conn: the connection that plan is for. @@ -418,12 +439,6 @@ struct io_plan *io_connect_(struct io_conn *conn, const struct addrinfo *addr, * * Note that if either plan closes the connection, it will be closed. * - * Note that if one plan is io_wait or io_always, that causes a problem: - * they look at the input and output plan slots to figure out which to - * use, but if the other plan hasn't been evaluated yet, that will fail. - * In this case, you'll need to ensure the other plan is evaluated first, - * eg. "struct io_plan *r = io_read(...); return io_duplex(r, io_always(...))" - * * Example: * struct buf { * char in[100]; @@ -501,6 +516,30 @@ struct io_plan *io_wait_(struct io_conn *conn, void *arg); +/** + * io_out_wait - leave the output plan idle until something wakes us. + * @conn: the connection that plan is for. + * @waitaddr: the address to wait on. + * @next: function to call after waiting. + * @arg: @next argument + * + * io_wait() makes the input plan idle: if you're not using io_duplex it + * doesn't matter which plan is waiting. Otherwise, you may need to use + * io_out_wait() instead, to specify explicitly that the output plan is + * waiting. + */ +#define io_out_wait(conn, waitaddr, next, arg) \ + io_out_wait_((conn), (waitaddr), \ + typesafe_cb_preargs(struct io_plan *, void *, \ + (next), (arg), \ + struct io_conn *), \ + (arg)) + +struct io_plan *io_out_wait_(struct io_conn *conn, + const void *wait, + struct io_plan *(*next)(struct io_conn *, void *), + void *arg); + /** * io_wake - wake up any connections waiting on @wait * @waitaddr: the address to trigger. diff --git a/ccan/io/test/run-12-bidir.c b/ccan/io/test/run-12-bidir.c index 10f84295..e79acb56 100644 --- a/ccan/io/test/run-12-bidir.c +++ b/ccan/io/test/run-12-bidir.c @@ -24,10 +24,20 @@ static void finish_ok(struct io_conn *conn, struct data *d) d->state++; } -static struct io_plan *rw_done(struct io_conn *conn, struct data *d) +static struct io_plan *r_done(struct io_conn *conn, struct data *d) { d->state++; - return io_halfclose(conn); + if (d->state == 3) + return io_close(conn); + return io_wait(conn, NULL, io_never, NULL); +} + +static struct io_plan *w_done(struct io_conn *conn, struct data *d) +{ + d->state++; + if (d->state == 3) + return io_close(conn); + return io_out_wait(conn, NULL, io_never, NULL); } static struct io_plan *init_conn(struct io_conn *conn, struct data *d) @@ -44,8 +54,8 @@ static struct io_plan *init_conn(struct io_conn *conn, struct data *d) io_set_finish(conn, finish_ok, d); return io_duplex(conn, - io_read(conn, d->buf, sizeof(d->buf), rw_done, d), - io_write(conn, d->wbuf, sizeof(d->wbuf), rw_done, d)); + io_read(conn, d->buf, sizeof(d->buf), r_done, d), + io_write(conn, d->wbuf, sizeof(d->wbuf), w_done, d)); } static int make_listen_fd(const char *port, struct addrinfo **info)