From d8ecdc8021b532fe76f001290fcd79c4fb9e1323 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 4 Aug 2014 17:46:21 +0930 Subject: [PATCH] ccan/io: io_halfclose. Helper for a common case. Replace all but 1 in tests. Signed-off-by: Rusty Russell --- ccan/io/io.c | 18 +++++++++++++++++ ccan/io/io.h | 28 ++++++++++++++++++++++++++ ccan/io/test/run-12-bidir.c | 10 ++------- ccan/io/test/run-14-duplex-both-read.c | 3 ++- ccan/io/test/run-16-duplex-test.c | 4 +--- 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/ccan/io/io.c b/ccan/io/io.c index 6d1c0522..7f5113d3 100644 --- a/ccan/io/io.c +++ b/ccan/io/io.c @@ -437,6 +437,24 @@ struct io_plan *io_duplex_(struct io_plan *in_plan, struct io_plan *out_plan) return out_plan + 1; } +struct io_plan *io_halfclose(struct io_conn *conn) +{ + /* Already closing? Don't close twice. */ + if (conn->plan[IO_IN].status == IO_CLOSING) + return &conn->plan[IO_IN]; + + /* Both unset? OK. */ + if (conn->plan[IO_IN].status == IO_UNSET + && conn->plan[IO_OUT].status == IO_UNSET) + return io_close(conn); + + /* We leave this unset then. */ + if (conn->plan[IO_IN].status == IO_UNSET) + return &conn->plan[IO_IN]; + else + return &conn->plan[IO_OUT]; +} + struct io_plan *io_set_plan(struct io_conn *conn, enum io_direction dir, int (*io)(int fd, struct io_plan_arg *arg), struct io_plan *(*next)(struct io_conn *, void *), diff --git a/ccan/io/io.h b/ccan/io/io.h index 1341b6f2..d0c0a39a 100644 --- a/ccan/io/io.h +++ b/ccan/io/io.h @@ -443,6 +443,34 @@ struct io_plan *io_connect_(struct io_conn *conn, const struct addrinfo *addr, struct io_plan *io_duplex_(struct io_plan *in_plan, struct io_plan *out_plan); void io_duplex_prepare(struct io_conn *conn); +/** + * io_halfclose - close half of an io_duplex connection. + * @conn: the connection that plan is for. + * + * It's common to want to close a duplex connection after both input and + * output plans have completed. If either calls io_close() the connection + * closes immediately. Instead, io_halfclose() needs to be called twice. + * + * Example: + * struct buf { + * char in[100]; + * char out[100]; + * }; + * + * static struct io_plan *finish(struct io_conn *conn, struct buf *b) + * { + * return io_halfclose(conn); + * } + * + * static struct io_plan *read_and_write(struct io_conn *conn, struct buf *b) + * { + * return io_duplex(conn, + * io_read(conn, b->in, sizeof(b->in), finish, b), + * io_write(conn, b->out, sizeof(b->out), finish, b)); + * } + */ +struct io_plan *io_halfclose(struct io_conn *conn); + /** * io_wait - leave a plan idle until something wakes us. * @conn: the connection that plan is for. diff --git a/ccan/io/test/run-12-bidir.c b/ccan/io/test/run-12-bidir.c index 2ecc5470..10f84295 100644 --- a/ccan/io/test/run-12-bidir.c +++ b/ccan/io/test/run-12-bidir.c @@ -15,7 +15,6 @@ struct data { struct io_listener *l; int state; - int done; char buf[4]; char wbuf[32]; }; @@ -28,10 +27,7 @@ static void finish_ok(struct io_conn *conn, struct data *d) static struct io_plan *rw_done(struct io_conn *conn, struct data *d) { d->state++; - d->done++; - if (d->done == 2) - return io_close(conn); - return io_wait(conn, NULL, io_never, NULL); + return io_halfclose(conn); } static struct io_plan *init_conn(struct io_conn *conn, struct data *d) @@ -91,9 +87,8 @@ int main(void) int fd, status; /* This is how many tests you plan to run */ - plan_tests(10); + plan_tests(9); d->state = 0; - d->done = 0; fd = make_listen_fd(PORT, &addrinfo); ok1(fd >= 0); d->l = io_new_listener(NULL, fd, init_conn, d); @@ -127,7 +122,6 @@ int main(void) freeaddrinfo(addrinfo); ok1(io_loop(NULL, NULL) == NULL); ok1(d->state == 4); - ok1(d->done == 2); ok1(memcmp(d->buf, "hellothere", sizeof(d->buf)) == 0); free(d); diff --git a/ccan/io/test/run-14-duplex-both-read.c b/ccan/io/test/run-14-duplex-both-read.c index ac334e78..30c46cd5 100644 --- a/ccan/io/test/run-14-duplex-both-read.c +++ b/ccan/io/test/run-14-duplex-both-read.c @@ -29,10 +29,11 @@ static void finish_ok(struct io_conn *conn, struct data *d) static struct io_plan *end(struct io_conn *conn, struct data *d) { d->state++; + /* Close on top of halfclose should work. */ if (d->state == 4) return io_close(conn); else - return io_wait(conn, NULL, io_never, NULL); + return io_halfclose(conn); } static struct io_plan *make_duplex(struct io_conn *conn, struct data *d) diff --git a/ccan/io/test/run-16-duplex-test.c b/ccan/io/test/run-16-duplex-test.c index 20814183..8631be45 100644 --- a/ccan/io/test/run-16-duplex-test.c +++ b/ccan/io/test/run-16-duplex-test.c @@ -29,9 +29,7 @@ static void finish_ok(struct io_conn *conn, struct data *d) static struct io_plan *io_done(struct io_conn *conn, struct data *d) { d->state++; - if (d->state == 3) - return io_close(conn); - return io_wait(conn, d, io_close_cb, NULL); + return io_halfclose(conn); } static struct io_plan *init_conn(struct io_conn *conn, struct data *d) -- 2.39.2