Helper for a common case. Replace all but 1 in tests.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
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 *),
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.
struct data {
struct io_listener *l;
int state;
- int done;
char buf[4];
char wbuf[32];
};
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)
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);
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);
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)
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)